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 "CompoundType.h"
29 #include "Coordinator.h"
30 #include "DocComment.h"
31 #include "Interface.h"
32
33 using namespace android;
34
usage(const char * me)35 static void usage(const char* me) {
36 Formatter out(stderr);
37
38 out << "Usage: " << me << " [-fh] [-o <output path>] [-l <header file>] ";
39 Coordinator::emitOptionsUsageString(out);
40 out << " FQNAME\n\n";
41
42 out << "Converts FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)? to an aidl "
43 "equivalent.\n\n";
44
45 out.indent();
46 out.indent();
47
48 out << "-f: Force hidl2aidl to convert older packages\n";
49 out << "-e: Used for expanding extensions and types from other packages\n";
50 out << "-h: Prints this menu.\n";
51 out << "-o <output path>: Location to output files.\n";
52 out << "-l <header file>: File containing a header to prepend to generated files.\n";
53 Coordinator::emitOptionsDetailString(out);
54
55 out.unindent();
56 out.unindent();
57 }
58
getNewerFQName(const FQName & lhs,const FQName & rhs)59 static const FQName& getNewerFQName(const FQName& lhs, const FQName& rhs) {
60 CHECK(lhs.package() == rhs.package());
61 CHECK(lhs.name() == rhs.name());
62
63 if (lhs.getPackageMajorVersion() > rhs.getPackageMajorVersion()) return lhs;
64 if (lhs.getPackageMajorVersion() < rhs.getPackageMajorVersion()) return rhs;
65
66 if (lhs.getPackageMinorVersion() > rhs.getPackageMinorVersion()) return lhs;
67 return rhs;
68 }
69
70 // If similar FQName is not found, the same one is returned
getLatestMinorVersionFQNameFromList(const FQName & fqName,const std::vector<FQName> & list)71 static FQName getLatestMinorVersionFQNameFromList(const FQName& fqName,
72 const std::vector<FQName>& list) {
73 FQName currentCandidate = fqName;
74 bool found = false;
75 for (const FQName& current : list) {
76 if (current.package() == currentCandidate.package() &&
77 current.name() == currentCandidate.name() &&
78 current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) {
79 // Prioritize elements in the list over the provided fqName
80 currentCandidate = found ? getNewerFQName(current, currentCandidate) : current;
81 found = true;
82 }
83 }
84
85 return currentCandidate;
86 }
87
getLatestMinorVersionNamedTypeFromList(const FQName & fqName,const std::set<const NamedType * > & list)88 static FQName getLatestMinorVersionNamedTypeFromList(const FQName& fqName,
89 const std::set<const NamedType*>& list) {
90 FQName currentCandidate = fqName;
91 bool found = false;
92 for (const NamedType* currentNamedType : list) {
93 const FQName& current = currentNamedType->fqName();
94 if (current.package() == currentCandidate.package() &&
95 current.name() == currentCandidate.name() &&
96 current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) {
97 // Prioritize elements in the list over the provided fqName
98 currentCandidate = found ? getNewerFQName(current, currentCandidate) : current;
99 found = true;
100 }
101 }
102
103 return currentCandidate;
104 }
105
packageExists(const Coordinator & coordinator,const FQName & fqName)106 static bool packageExists(const Coordinator& coordinator, const FQName& fqName) {
107 bool result;
108 status_t err = coordinator.packageExists(fqName, &result);
109 if (err != OK) {
110 std::cerr << "Error trying to find package " << fqName.string() << std::endl;
111 exit(1);
112 }
113
114 return result;
115 }
116
117 // assuming fqName exists, find oldest version which does exist
118 // e.g. android.hardware.foo@1.7 -> android.hardware.foo@1.1 (if foo@1.0 doesn't
119 // exist)
getLowestExistingFqName(const Coordinator & coordinator,const FQName & fqName)120 static FQName getLowestExistingFqName(const Coordinator& coordinator, const FQName& fqName) {
121 FQName lowest(fqName);
122 while (lowest.getPackageMinorVersion() != 0) {
123 if (!packageExists(coordinator, lowest.downRev())) break;
124
125 lowest = lowest.downRev();
126 }
127 return lowest;
128 }
129
130 // assuming fqName exists, find newest version which does exist
131 // e.g. android.hardware.foo@1.1 -> android.hardware.foo@1.7 if that's the
132 // newest
getHighestExistingFqName(const Coordinator & coordinator,const FQName & fqName)133 static FQName getHighestExistingFqName(const Coordinator& coordinator, const FQName& fqName) {
134 FQName highest(fqName);
135 while (packageExists(coordinator, highest.upRev())) {
136 highest = highest.upRev();
137 }
138 return highest;
139 }
140
parse(const Coordinator & coordinator,const FQName & target)141 static AST* parse(const Coordinator& coordinator, const FQName& target) {
142 AST* ast = coordinator.parse(target);
143 if (ast == nullptr) {
144 std::cerr << "ERROR: Could not parse " << target.name() << ". Aborting." << std::endl;
145 exit(1);
146 }
147
148 if (!ast->getUnhandledComments().empty()) {
149 AidlHelper::notes()
150 << "Unhandled comments from " << target.string()
151 << " follow. Consider using hidl-lint to locate these and fixup as many "
152 << "as possible.\n";
153 for (const DocComment* docComment : ast->getUnhandledComments()) {
154 docComment->emit(AidlHelper::notes());
155 }
156 AidlHelper::notes() << "\n";
157 }
158
159 return ast;
160 }
161
getSubTypes(const NamedType & namedType,std::set<const NamedType * > * types)162 static void getSubTypes(const NamedType& namedType, std::set<const NamedType*>* types) {
163 if (namedType.isScope()) {
164 const Scope& compoundType = static_cast<const Scope&>(namedType);
165 for (const NamedType* subType : compoundType.getSubTypes()) {
166 types->insert(subType);
167 getSubTypes(*subType, types);
168 }
169 }
170 }
171
emitAidlSharedLibs(Formatter & out,FQName fqName,AidlBackend backend)172 static void emitAidlSharedLibs(Formatter& out, FQName fqName, AidlBackend backend) {
173 if (backend == AidlBackend::NDK) {
174 out << " \"libbinder_ndk\",\n";
175 out << " \"libhidlbase\",\n";
176 out << " \"" << AidlHelper::getAidlPackage(fqName) << "-V1-ndk_platform\",\n";
177 } else if (backend == AidlBackend::CPP) {
178 out << " \"libbinder\",\n";
179 out << " \"libhidlbase\",\n";
180 out << " \"" << AidlHelper::getAidlPackage(fqName) << "-V1-cpp\",\n";
181 out << " \"libutils\",\n";
182 } else {
183 out << " \"" << AidlHelper::getAidlPackage(fqName) << "-V1-java\",\n";
184 }
185 }
186
emitHidlSharedLibs(Formatter & out,std::vector<FQName> & targets,AidlBackend backend)187 static void emitHidlSharedLibs(Formatter& out, std::vector<FQName>& targets, AidlBackend backend) {
188 std::set<std::string> uniquePackages;
189 for (const auto& target : targets) {
190 if (backend == AidlBackend::JAVA) {
191 uniquePackages.insert(
192 android::base::StringReplace(target.getPackageAndVersion().string(), "@", "-V",
193 false /* all */) +
194 "-java");
195 } else {
196 uniquePackages.insert(target.getPackageAndVersion().string());
197 }
198 }
199 for (const auto& package : uniquePackages) {
200 out << " \"" << package << "\",\n";
201 }
202 }
203
aidlTranslateLibraryName(FQName fqName,AidlBackend backend)204 static std::string aidlTranslateLibraryName(FQName fqName, AidlBackend backend) {
205 std::string postfix;
206 if (backend == AidlBackend::NDK) {
207 postfix = "-ndk";
208 } else if (backend == AidlBackend::CPP) {
209 postfix = "-cpp";
210 } else {
211 postfix = "-java";
212 }
213 return AidlHelper::getAidlPackage(fqName) + "-translate" + postfix;
214 }
215
emitBuildFile(Formatter & out,const FQName & fqName,std::vector<FQName> & targets,bool needsTranslation)216 static void emitBuildFile(Formatter& out, const FQName& fqName, std::vector<FQName>& targets,
217 bool needsTranslation) {
218 out << "// This is the expected build file, but it may not be right in all cases\n";
219 out << "\n";
220 out << "aidl_interface {\n";
221 out << " name: \"" << AidlHelper::getAidlPackage(fqName) << "\",\n";
222 out << " vendor_available: true,\n";
223 out << " srcs: [\"" << AidlHelper::getAidlPackagePath(fqName) << "/*.aidl\"],\n";
224 out << " stability: \"vintf\",\n";
225 out << " backend: {\n";
226 out << " cpp: {\n";
227 out << " // FIXME should this be disabled?\n";
228 out << " // prefer NDK backend which can be used anywhere\n";
229 out << " enabled: true,\n";
230 out << " },\n";
231 out << " java: {\n";
232 out << " sdk_version: \"module_current\",\n";
233 out << " },\n";
234 out << " ndk: {\n";
235 out << " vndk: {\n";
236 out << " enabled: true,\n";
237 out << " },\n";
238 out << " },\n";
239 out << " },\n";
240 out << "}\n\n";
241
242 if (!needsTranslation) return;
243
244 for (auto backend : {AidlBackend::CPP, AidlBackend::NDK}) {
245 out << "cc_library {\n";
246 out << " name: \"" << aidlTranslateLibraryName(fqName, backend) << +"\",\n";
247 if (backend == AidlBackend::NDK) {
248 out << " vendor_available: true,\n";
249 }
250 out << " srcs: [\"" << AidlHelper::translateSourceFile(fqName, backend) + "\"],\n";
251 out << " shared_libs: [\n";
252 emitAidlSharedLibs(out, fqName, backend);
253 emitHidlSharedLibs(out, targets, backend);
254 out << " ],\n";
255 out << " export_include_dirs: [\"include\"],\n";
256 out << "}\n\n";
257 }
258
259 out << "java_library {\n";
260 out << " name: \"" << aidlTranslateLibraryName(fqName, AidlBackend::JAVA) << +"\",\n";
261 out << " srcs: [\"" << AidlHelper::translateSourceFile(fqName, AidlBackend::JAVA) + "\"],\n";
262 out << " libs: [\n";
263 emitAidlSharedLibs(out, fqName, AidlBackend::JAVA);
264 emitHidlSharedLibs(out, targets, AidlBackend::JAVA);
265 out << " ],\n";
266 out << " sdk_version: \"module_current\",\n";
267 out << "}\n\n";
268 }
269
270 // hidl is intentionally leaky. Turn off LeakSanitizer by default.
__asan_default_options()271 extern "C" const char* __asan_default_options() {
272 return "detect_leaks=0";
273 }
274
main(int argc,char ** argv)275 int main(int argc, char** argv) {
276 const char* me = argv[0];
277 if (argc == 1) {
278 usage(me);
279 std::cerr << "ERROR: no fqname specified." << std::endl;
280 exit(1);
281 }
282
283 Coordinator coordinator;
284 std::string outputPath;
285 std::string fileHeader;
286 bool forceConvertOldInterfaces = false;
287 coordinator.parseOptions(argc, argv, "fho:l:e", [&](int res, char* arg) {
288 switch (res) {
289 case 'o': {
290 if (!outputPath.empty()) {
291 fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
292 exit(1);
293 }
294 outputPath = arg;
295 break;
296 }
297 case 'l':
298 if (!fileHeader.empty()) {
299 fprintf(stderr, "ERROR: -l <header file> can only be specified once.\n");
300 exit(1);
301 }
302 fileHeader = arg;
303 break;
304 case 'f':
305 forceConvertOldInterfaces = true;
306 break;
307 case 'e':
308 AidlHelper::setExpandExtended(true);
309 break;
310 case 'h':
311 case '?':
312 default: {
313 usage(me);
314 exit(1);
315 break;
316 }
317 }
318 });
319
320 if (!outputPath.empty() && outputPath.back() != '/') {
321 outputPath += "/";
322 }
323 coordinator.setOutputPath(outputPath);
324 AidlHelper::setFileHeader(fileHeader);
325
326 argc -= optind;
327 argv += optind;
328
329 if (argc == 0) {
330 usage(me);
331 std::cerr << "ERROR: no fqname specified." << std::endl;
332 exit(1);
333 }
334
335 if (argc > 1) {
336 usage(me);
337 std::cerr << "ERROR: only one fqname can be specified." << std::endl;
338 exit(1);
339 }
340
341 const char* arg = argv[0];
342
343 FQName fqName;
344 if (!FQName::parse(arg, &fqName)) {
345 std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "." << std::endl;
346 exit(1);
347 }
348
349 if (fqName.isFullyQualified()) {
350 std::cerr << "ERROR: hidl2aidl only supports converting an entire package, try "
351 "converting "
352 << fqName.getPackageAndVersion().string() << " instead." << std::endl;
353 exit(1);
354 }
355
356 if (!packageExists(coordinator, fqName)) {
357 std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl;
358 exit(1);
359 }
360
361 if (!forceConvertOldInterfaces) {
362 const FQName highestFqName = getHighestExistingFqName(coordinator, fqName);
363 if (fqName != highestFqName) {
364 std::cerr << "ERROR: A newer minor version of " << fqName.string() << " exists ("
365 << highestFqName.string()
366 << "). In general, prefer to convert that instead. If you really mean to "
367 "use an old minor version use '-f'."
368 << std::endl;
369 exit(1);
370 }
371 }
372
373 // This is the list of all types which should be converted
374 std::vector<FQName> targets;
375 for (FQName version = getLowestExistingFqName(coordinator, fqName);
376 version.getPackageMinorVersion() <= fqName.getPackageMinorVersion();
377 version = version.upRev()) {
378 std::vector<FQName> newTargets;
379 status_t err = coordinator.appendPackageInterfacesToVector(version, &newTargets);
380 if (err != OK) exit(1);
381
382 targets.insert(targets.end(), newTargets.begin(), newTargets.end());
383 }
384
385 // targets should not contain duplicates since appendPackageInterfaces is only called once
386 // per version. now remove all the elements that are not the "newest"
387 const auto& newEnd =
388 std::remove_if(targets.begin(), targets.end(), [&](const FQName& fqName) -> bool {
389 if (fqName.name() == "types") return false;
390
391 return getLatestMinorVersionFQNameFromList(fqName, targets) != fqName;
392 });
393 targets.erase(newEnd, targets.end());
394
395 // Set up AIDL conversion log
396 Formatter err =
397 coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "conversion.log");
398 err << "Notes relating to hidl2aidl conversion of " << fqName.string() << " to "
399 << AidlHelper::getAidlPackage(fqName) << " (if any) follow:\n";
400 AidlHelper::setNotes(&err);
401
402 // Gather all the types and interfaces
403 std::set<const NamedType*> namedTypesInPackage;
404 for (const FQName& target : targets) {
405
406 AST* ast = parse(coordinator, target);
407 CHECK(ast);
408
409 const Interface* iface = ast->getInterface();
410 if (iface) {
411 namedTypesInPackage.insert(iface);
412
413 // Get all of the types defined in the interface chain(includes self)
414 for (const Interface* interface : iface->typeChain()) {
415 if (!AidlHelper::shouldBeExpanded(iface->fqName(), interface->fqName())) {
416 break;
417 }
418 getSubTypes(*interface, &namedTypesInPackage);
419 }
420 } else {
421 getSubTypes(ast->getRootScope(), &namedTypesInPackage);
422 }
423 }
424
425 // Remove all of the older versions of types and keep the latest
426 for (auto it = namedTypesInPackage.begin(); it != namedTypesInPackage.end();) {
427 if (getLatestMinorVersionNamedTypeFromList((*it)->fqName(), namedTypesInPackage) !=
428 (*it)->fqName()) {
429 it = namedTypesInPackage.erase(it);
430 } else {
431 it++;
432 }
433 }
434
435 // Process and flatten all of the types. Many types include fields of older
436 // versions of that type.
437 // This step recursively finds all of those fields and adds their fields to
438 // the most recent top level type.
439 std::map<const NamedType*, const ProcessedCompoundType> processedTypesInPackage;
440 for (const auto& namedType : namedTypesInPackage) {
441 if (namedType->isCompoundType()) {
442 ProcessedCompoundType processed;
443 AidlHelper::processCompoundType(static_cast<const CompoundType&>(*namedType),
444 &processed, std::string());
445 processedTypesInPackage.insert(
446 std::pair<const NamedType*, const ProcessedCompoundType>(namedType, processed));
447 }
448 }
449
450 Formatter buildFile =
451 coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "Android.bp");
452 emitBuildFile(buildFile, fqName, targets, !processedTypesInPackage.empty());
453 AidlHelper::emitTranslation(coordinator, fqName, namedTypesInPackage, processedTypesInPackage);
454
455 // Emit all types and interfaces
456 // The interfaces and types are still be further manipulated inside
457 // emitAidl. The interfaces are consolidating methods from their typechains
458 // and the composite types are being flattened.
459 for (const auto& namedType : namedTypesInPackage) {
460 AidlHelper::emitAidl(*namedType, coordinator, processedTypesInPackage);
461 }
462
463 err << "END OF LOG\n";
464
465 return 0;
466 }
467