1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android-base/logging.h>
18 #include <android-base/strings.h>
19 #include <hidl-util/FQName.h>
20 #include <hidl-util/Formatter.h>
21
22 #include <algorithm>
23 #include <iostream>
24 #include <vector>
25
26 #include "AST.h"
27 #include "AidlHelper.h"
28 #include "Coordinator.h"
29 #include "DocComment.h"
30
31 using namespace android;
32
usage(const char * me)33 static void usage(const char* me) {
34 Formatter out(stderr);
35
36 out << "Usage: " << me << " [-o <output path>] ";
37 Coordinator::emitOptionsUsageString(out);
38 out << " FQNAME...\n\n";
39
40 out << "Converts FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)? to an aidl "
41 "equivalent.\n\n";
42
43 out.indent();
44 out.indent();
45
46 out << "-o <output path>: Location to output files.\n";
47 out << "-h: Prints this menu.\n";
48 Coordinator::emitOptionsDetailString(out);
49
50 out.unindent();
51 out.unindent();
52 }
53
getNewerFQName(const FQName & lhs,const FQName & rhs)54 static const FQName& getNewerFQName(const FQName& lhs, const FQName& rhs) {
55 CHECK(lhs.package() == rhs.package());
56 CHECK(lhs.name() == rhs.name());
57
58 if (lhs.getPackageMajorVersion() > rhs.getPackageMajorVersion()) return lhs;
59 if (lhs.getPackageMajorVersion() < rhs.getPackageMajorVersion()) return rhs;
60
61 if (lhs.getPackageMinorVersion() > rhs.getPackageMinorVersion()) return lhs;
62 return rhs;
63 }
64
65 // If similar FQName is not found, the same one is returned
getLatestMinorVersionFQNameFromList(const FQName & fqName,const std::vector<FQName> & list)66 static FQName getLatestMinorVersionFQNameFromList(const FQName& fqName,
67 const std::vector<FQName>& list) {
68 FQName currentCandidate = fqName;
69 bool found = false;
70 for (const FQName& current : list) {
71 if (current.package() == currentCandidate.package() &&
72 current.name() == currentCandidate.name() &&
73 current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) {
74 // Prioritize elements in the list over the provided fqName
75 currentCandidate = found ? getNewerFQName(current, currentCandidate) : current;
76 found = true;
77 }
78 }
79
80 return currentCandidate;
81 }
82
getLatestMinorVersionNamedTypeFromList(const FQName & fqName,const std::vector<const NamedType * > & list)83 static FQName getLatestMinorVersionNamedTypeFromList(const FQName& fqName,
84 const std::vector<const NamedType*>& list) {
85 FQName currentCandidate = fqName;
86 bool found = false;
87 for (const NamedType* currentNamedType : list) {
88 const FQName& current = currentNamedType->fqName();
89 if (current.package() == currentCandidate.package() &&
90 current.name() == currentCandidate.name() &&
91 current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) {
92 // Prioritize elements in the list over the provided fqName
93 currentCandidate = found ? getNewerFQName(current, currentCandidate) : current;
94 found = true;
95 }
96 }
97
98 return currentCandidate;
99 }
100
packageExists(const Coordinator & coordinator,const FQName & fqName)101 static bool packageExists(const Coordinator& coordinator, const FQName& fqName) {
102 bool result;
103 status_t err = coordinator.packageExists(fqName, &result);
104 if (err != OK) {
105 std::cerr << "Error trying to find package " << fqName.string() << std::endl;
106 exit(1);
107 }
108
109 return result;
110 }
111
parse(const Coordinator & coordinator,const FQName & target)112 static AST* parse(const Coordinator& coordinator, const FQName& target) {
113 AST* ast = coordinator.parse(target);
114 if (ast == nullptr) {
115 std::cerr << "ERROR: Could not parse " << target.name() << ". Aborting." << std::endl;
116 exit(1);
117 }
118
119 if (!ast->getUnhandledComments().empty()) {
120 AidlHelper::notes()
121 << "Unhandled comments from " << target.string()
122 << " follow. Consider using hidl-lint to locate these and fixup as many "
123 << "as possible.\n";
124 for (const DocComment* docComment : ast->getUnhandledComments()) {
125 docComment->emit(AidlHelper::notes());
126 }
127 AidlHelper::notes() << "\n";
128 }
129
130 return ast;
131 }
132
133 // hidl is intentionally leaky. Turn off LeakSanitizer by default.
__asan_default_options()134 extern "C" const char* __asan_default_options() {
135 return "detect_leaks=0";
136 }
137
main(int argc,char ** argv)138 int main(int argc, char** argv) {
139 const char* me = argv[0];
140 if (argc == 1) {
141 usage(me);
142 std::cerr << "ERROR: no fqname specified." << std::endl;
143 exit(1);
144 }
145
146 Coordinator coordinator;
147 std::string outputPath;
148 coordinator.parseOptions(argc, argv, "ho:", [&](int res, char* arg) {
149 switch (res) {
150 case 'o': {
151 if (!outputPath.empty()) {
152 fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
153 exit(1);
154 }
155 outputPath = arg;
156 break;
157 }
158 case 'h':
159 case '?':
160 default: {
161 usage(me);
162 exit(1);
163 break;
164 }
165 }
166 });
167
168 if (!outputPath.empty() && outputPath.back() != '/') {
169 outputPath += "/";
170 }
171 coordinator.setOutputPath(outputPath);
172
173 argc -= optind;
174 argv += optind;
175
176 if (argc == 0) {
177 usage(me);
178 std::cerr << "ERROR: no fqname specified." << std::endl;
179 exit(1);
180 }
181
182 for (int i = 0; i < argc; ++i) {
183 const char* arg = argv[i];
184
185 FQName fqName;
186 if (!FQName::parse(arg, &fqName)) {
187 std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "."
188 << std::endl;
189 exit(1);
190 }
191
192 if (!packageExists(coordinator, fqName)) {
193 std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl;
194 exit(1);
195 }
196
197 FQName currentFqName(fqName);
198 while (currentFqName.getPackageMinorVersion() != 0) {
199 if (!packageExists(coordinator, currentFqName.downRev())) break;
200
201 currentFqName = currentFqName.downRev();
202 }
203
204 std::vector<FQName> targets;
205 while (packageExists(coordinator, currentFqName)) {
206 std::vector<FQName> newTargets;
207 status_t err = coordinator.appendPackageInterfacesToVector(currentFqName, &newTargets);
208 if (err != OK) break;
209
210 targets.insert(targets.end(), newTargets.begin(), newTargets.end());
211
212 currentFqName = currentFqName.upRev();
213 }
214
215 // targets should not contain duplicates since appendPackageInterfaces is only called once
216 // per version. now remove all the elements that are not the "newest"
217 const auto& newEnd =
218 std::remove_if(targets.begin(), targets.end(), [&](const FQName& fqName) -> bool {
219 if (fqName.name() == "types") return false;
220
221 return getLatestMinorVersionFQNameFromList(fqName, targets) != fqName;
222 });
223 targets.erase(newEnd, targets.end());
224
225 if (fqName.isFullyQualified()) {
226 // Ensure that this fqName exists in the list.
227 // If not then there is a more recent version
228 if (std::find(targets.begin(), targets.end(), fqName) == targets.end()) {
229 // Not found. Error.
230 std::cerr << "ERROR: A newer minor version of " << fqName.string()
231 << " exists. Compile that instead." << std::endl;
232 exit(1);
233 } else {
234 targets.clear();
235 targets.push_back(fqName);
236 }
237 }
238
239 // Set up AIDL conversion log
240 std::string aidlPackage = AidlHelper::getAidlPackage(fqName);
241 std::string aidlName = AidlHelper::getAidlName(fqName);
242 Formatter err = coordinator.getFormatter(
243 fqName, Coordinator::Location::DIRECT,
244 base::Join(base::Split(aidlPackage, "."), "/") + "/" +
245 (aidlName.empty() ? "" : (aidlName + "-")) + "conversion.log");
246 AidlHelper::setNotes(&err);
247
248 std::vector<const NamedType*> namedTypesInPackage;
249 for (const FQName& target : targets) {
250 if (target.name() != "types") continue;
251
252 AST* ast = parse(coordinator, target);
253
254 CHECK(!ast->isInterface());
255
256 std::vector<const NamedType*> types = ast->getRootScope().getSortedDefinedTypes();
257 namedTypesInPackage.insert(namedTypesInPackage.end(), types.begin(), types.end());
258 }
259
260 const auto& endNamedTypes = std::remove_if(
261 namedTypesInPackage.begin(), namedTypesInPackage.end(),
262 [&](const NamedType* namedType) -> bool {
263 return getLatestMinorVersionNamedTypeFromList(
264 namedType->fqName(), namedTypesInPackage) != namedType->fqName();
265 });
266 namedTypesInPackage.erase(endNamedTypes, namedTypesInPackage.end());
267
268 for (const NamedType* namedType : namedTypesInPackage) {
269 AidlHelper::emitAidl(*namedType, coordinator);
270 }
271
272 for (const FQName& target : targets) {
273 if (target.name() == "types") continue;
274
275 AST* ast = parse(coordinator, target);
276
277 const Interface* iface = ast->getInterface();
278 CHECK(iface);
279
280 AidlHelper::emitAidl(*iface, coordinator);
281 }
282 }
283
284 return 0;
285 }
286