1 // Copyright (C) 2016 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma clang diagnostic push
16 #pragma clang diagnostic ignored "-Wunused-parameter"
17 #pragma clang diagnostic ignored "-Wnested-anon-types"
18 #include "proto/abi_dump.pb.h"
19 #include "proto/abi_diff.pb.h"
20 #pragma clang diagnostic pop
21 
22 #include <header_abi_util.h>
23 
24 #include <llvm/Support/CommandLine.h>
25 #include <llvm/Support/raw_ostream.h>
26 
27 #include <google/protobuf/text_format.h>
28 #include <google/protobuf/io/zero_copy_stream_impl.h>
29 
30 #include <memory>
31 #include <fstream>
32 #include <iostream>
33 #include <string>
34 #include <vector>
35 
36 #include <stdlib.h>
37 
38 static llvm::cl::OptionCategory merge_abi_diff_category(
39     "merge-abi-diff options");
40 
41 static llvm::cl::list<std::string> diff_report_list(
42     llvm::cl::Positional, llvm::cl::desc("<diff-reports>"), llvm::cl::Required,
43     llvm::cl::cat(merge_abi_diff_category), llvm::cl::OneOrMore);
44 
45 static llvm::cl::opt<std::string> merged_diff_report(
46     "o", llvm::cl::desc("<merged-diff-report>"), llvm::cl::Required,
47     llvm::cl::cat(merge_abi_diff_category));
48 
49 static llvm::cl::opt<bool> advice_only(
50     "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
51     llvm::cl::cat(merge_abi_diff_category));
52 
53 static llvm::cl::opt<bool> do_not_break_on_extensions(
54     "allow-extensions",
55     llvm::cl::desc("Do not return a non zero status on extensions"),
56     llvm::cl::Optional, llvm::cl::cat(merge_abi_diff_category));
57 
58 typedef abi_diff::CompatibilityStatus CompatibilityStatus;
59 
IsStatusDowngraded(const CompatibilityStatus & old_status,const CompatibilityStatus & new_status)60 static bool IsStatusDowngraded(const CompatibilityStatus &old_status,
61                                const CompatibilityStatus &new_status) {
62   bool status_downgraded = false;
63   switch (old_status) {
64     case CompatibilityStatus::EXTENSION:
65       if (new_status == CompatibilityStatus::INCOMPATIBLE) {
66         status_downgraded = true;
67       }
68       break;
69     case CompatibilityStatus::COMPATIBLE:
70       if (new_status != CompatibilityStatus::COMPATIBLE) {
71         status_downgraded = true;
72       }
73       break;
74     default:
75       break;
76   }
77   return status_downgraded;
78 }
79 
MergeDiffReports(const std::vector<std::string> & diff_reports,const std::string & merged_diff_report)80 static CompatibilityStatus MergeDiffReports(
81     const std::vector<std::string> &diff_reports,
82     const std::string &merged_diff_report) {
83 
84   abi_diff::MergedTranslationUnitDiff merged_tu_diff;
85   std::ofstream text_output(merged_diff_report);
86   google::protobuf::io::OstreamOutputStream text_os(&text_output);
87   CompatibilityStatus status = CompatibilityStatus::COMPATIBLE;
88 
89   for (auto &&i : diff_reports) {
90     abi_diff::TranslationUnitDiff diff_tu;
91     std::ifstream input(i);
92     google::protobuf::io::IstreamInputStream text_is(&input);
93     if (!google::protobuf::TextFormat::Parse(&text_is, &diff_tu)) {
94       llvm::errs() << "Failed to parse diff report\n";
95       ::exit(1);
96     }
97     abi_diff::ConciseDiffReportInformation *added_tu_diff =
98         merged_tu_diff.add_diff_reports();
99     if (!added_tu_diff) {
100       llvm::errs() << "Failed to add diff report to merged report\n";
101       ::exit(1);
102     }
103     CompatibilityStatus new_status = diff_tu.compatibility_status();
104     added_tu_diff->set_lib_name(diff_tu.lib_name());
105     added_tu_diff->set_arch(diff_tu.arch());
106     added_tu_diff->set_diff_report_path(i);
107     added_tu_diff->set_compatibility_status(new_status);
108     // Only, if the status is downgraded, change it.
109     if (IsStatusDowngraded(status, new_status)) {
110       status = new_status;
111     }
112   }
113 
114   if (!google::protobuf::TextFormat::Print(merged_tu_diff, &text_os)) {
115     llvm::errs() << "Serialization to ostream failed\n";
116     ::exit(1);
117   }
118   return status;
119 }
120 
main(int argc,const char ** argv)121 int main(int argc, const char **argv) {
122   GOOGLE_PROTOBUF_VERIFY_VERSION;
123   llvm::cl::ParseCommandLineOptions(argc, argv, "merge-abi-diff");
124   CompatibilityStatus extension_or_incompatible =
125       MergeDiffReports(diff_report_list, merged_diff_report);
126   std::string status_str = "";
127   switch (extension_or_incompatible) {
128     case CompatibilityStatus::INCOMPATIBLE:
129       status_str = "broken";
130       break;
131     case CompatibilityStatus::EXTENSION:
132       status_str = "extended";
133       break;
134     default:
135       break;
136   }
137   if (extension_or_incompatible) {
138     llvm::errs() << "******************************************************\n"
139                  << "VNDK Abi "
140                  << status_str
141                  << ":"
142                  << " Please check compatiblity report at : "
143                  << merged_diff_report << "\n"
144                  << "*****************************************************\n";
145   }
146 
147   if (do_not_break_on_extensions &&
148       extension_or_incompatible == CompatibilityStatus::EXTENSION) {
149       extension_or_incompatible = CompatibilityStatus::COMPATIBLE;
150   }
151 
152   if (!advice_only) {
153     return extension_or_incompatible;
154   }
155   return CompatibilityStatus::COMPATIBLE;
156 }
157