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 #include "repr/protobuf/abi_diff.h"
16 #include "repr/protobuf/abi_dump.h"
17 
18 #include <google/protobuf/text_format.h>
19 #include <google/protobuf/io/zero_copy_stream_impl.h>
20 
21 #include <llvm/Support/CommandLine.h>
22 #include <llvm/Support/raw_ostream.h>
23 
24 #include <fstream>
25 #include <iostream>
26 #include <memory>
27 #include <string>
28 #include <vector>
29 
30 #include <stdlib.h>
31 
32 
33 static llvm::cl::OptionCategory merge_diff_category(
34     "merge-abi-diff options");
35 
36 static llvm::cl::list<std::string> diff_report_list(
37     llvm::cl::Positional, llvm::cl::desc("<diff-reports>"), llvm::cl::Required,
38     llvm::cl::cat(merge_diff_category), llvm::cl::OneOrMore);
39 
40 static llvm::cl::opt<std::string> merged_diff_report(
41     "o", llvm::cl::desc("<merged-diff-report>"), llvm::cl::Required,
42     llvm::cl::cat(merge_diff_category));
43 
44 static llvm::cl::opt<bool> advice_only(
45     "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
46     llvm::cl::cat(merge_diff_category));
47 
48 static llvm::cl::opt<bool> do_not_break_on_extensions(
49     "allow-extensions",
50     llvm::cl::desc("Do not return a non zero status on extensions"),
51     llvm::cl::Optional, llvm::cl::cat(merge_diff_category));
52 
IsStatusDowngraded(const abi_diff::CompatibilityStatus & old_status,const abi_diff::CompatibilityStatus & new_status)53 static bool IsStatusDowngraded(
54     const abi_diff::CompatibilityStatus &old_status,
55     const abi_diff::CompatibilityStatus &new_status) {
56   bool status_downgraded = false;
57   switch (old_status) {
58     case abi_diff::CompatibilityStatus::EXTENSION:
59       if (new_status == abi_diff::CompatibilityStatus::INCOMPATIBLE) {
60         status_downgraded = true;
61       }
62       break;
63     case abi_diff::CompatibilityStatus::COMPATIBLE:
64       if (new_status != abi_diff::CompatibilityStatus::COMPATIBLE) {
65         status_downgraded = true;
66       }
67       break;
68     default:
69       break;
70   }
71   return status_downgraded;
72 }
73 
MergeDiffReports(const std::vector<std::string> & diff_reports,const std::string & merged_diff_report)74 static abi_diff::CompatibilityStatus MergeDiffReports(
75     const std::vector<std::string> &diff_reports,
76     const std::string &merged_diff_report) {
77   abi_diff::MergedTranslationUnitDiff merged_tu_diff;
78   std::ofstream text_output(merged_diff_report);
79   google::protobuf::io::OstreamOutputStream text_os(&text_output);
80   abi_diff::CompatibilityStatus status =
81       abi_diff::CompatibilityStatus::COMPATIBLE;
82 
83   for (auto &&i : diff_reports) {
84     abi_diff::TranslationUnitDiff diff_tu;
85     std::ifstream input(i);
86     google::protobuf::io::IstreamInputStream text_is(&input);
87     if (!google::protobuf::TextFormat::Parse(&text_is, &diff_tu)) {
88       llvm::errs() << "Failed to parse diff report\n";
89       ::exit(1);
90     }
91     abi_diff::ConciseDiffReportInformation *added_tu_diff =
92         merged_tu_diff.add_diff_reports();
93     if (!added_tu_diff) {
94       llvm::errs() << "Failed to add diff report to merged report\n";
95       ::exit(1);
96     }
97     abi_diff::CompatibilityStatus new_status = diff_tu.compatibility_status();
98     added_tu_diff->set_lib_name(diff_tu.lib_name());
99     added_tu_diff->set_arch(diff_tu.arch());
100     added_tu_diff->set_diff_report_path(i);
101     added_tu_diff->set_compatibility_status(new_status);
102     // Only, if the status is downgraded, change it.
103     if (IsStatusDowngraded(status, new_status)) {
104       status = new_status;
105     }
106   }
107 
108   if (!google::protobuf::TextFormat::Print(merged_tu_diff, &text_os)) {
109     llvm::errs() << "Serialization to ostream failed\n";
110     ::exit(1);
111   }
112   return status;
113 }
114 
main(int argc,const char ** argv)115 int main(int argc, const char **argv) {
116   GOOGLE_PROTOBUF_VERIFY_VERSION;
117   llvm::cl::ParseCommandLineOptions(argc, argv, "merge-abi-diff");
118   abi_diff::CompatibilityStatus extension_or_incompatible =
119       MergeDiffReports(diff_report_list, merged_diff_report);
120   std::string status_str = "";
121   switch (extension_or_incompatible) {
122     case abi_diff::CompatibilityStatus::INCOMPATIBLE:
123       status_str = "broken";
124       break;
125     case abi_diff::CompatibilityStatus::EXTENSION:
126       status_str = "extended";
127       break;
128     default:
129       break;
130   }
131   if (extension_or_incompatible) {
132     llvm::errs() << "******************************************************\n"
133                  << "VNDK Abi "
134                  << status_str
135                  << ":"
136                  << " Please check compatibility report at : "
137                  << merged_diff_report << "\n"
138                  << "******************************************************\n";
139   }
140 
141   if (do_not_break_on_extensions &&
142       extension_or_incompatible == abi_diff::CompatibilityStatus::EXTENSION) {
143       extension_or_incompatible = abi_diff::CompatibilityStatus::COMPATIBLE;
144   }
145 
146   if (!advice_only) {
147     return extension_or_incompatible;
148   }
149   return abi_diff::CompatibilityStatus::COMPATIBLE;
150 }
151