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