• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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\",\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 << "            // If you disable this, you also need to delete the C++\n";
230     out << "            // translate code.\n";
231     out << "            enabled: true,\n";
232     out << "        },\n";
233     out << "        java: {\n";
234     out << "            sdk_version: \"module_current\",\n";
235     out << "        },\n";
236     out << "        ndk: {\n";
237     out << "            vndk: {\n";
238     out << "                enabled: true,\n";
239     out << "            },\n";
240     out << "        },\n";
241     out << "    },\n";
242     out << "}\n\n";
243 
244     if (!needsTranslation) return;
245 
246     for (auto backend : {AidlBackend::CPP, AidlBackend::NDK}) {
247         out << "cc_library {\n";
248         out << "    name: \"" << aidlTranslateLibraryName(fqName, backend) << +"\",\n";
249         if (backend == AidlBackend::NDK) {
250             out << "    vendor_available: true,\n";
251         }
252         out << "    srcs: [\"" << AidlHelper::translateSourceFile(fqName, backend) + "\"],\n";
253         out << "    shared_libs: [\n";
254         emitAidlSharedLibs(out, fqName, backend);
255         emitHidlSharedLibs(out, targets, backend);
256         out << "    ],\n";
257         out << "    export_include_dirs: [\"include\"],\n";
258         out << "}\n\n";
259     }
260 
261     out << "java_library {\n";
262     out << "    name: \"" << aidlTranslateLibraryName(fqName, AidlBackend::JAVA) << +"\",\n";
263     out << "    srcs: [\"" << AidlHelper::translateSourceFile(fqName, AidlBackend::JAVA) + "\"],\n";
264     out << "    libs: [\n";
265     emitAidlSharedLibs(out, fqName, AidlBackend::JAVA);
266     emitHidlSharedLibs(out, targets, AidlBackend::JAVA);
267     out << "    ],\n";
268     out << "    sdk_version: \"module_current\",\n";
269     out << "}\n\n";
270 }
271 
272 // hidl is intentionally leaky. Turn off LeakSanitizer by default.
__asan_default_options()273 extern "C" const char* __asan_default_options() {
274     return "detect_leaks=0";
275 }
276 
main(int argc,char ** argv)277 int main(int argc, char** argv) {
278     const char* me = argv[0];
279     if (argc == 1) {
280         usage(me);
281         std::cerr << "ERROR: no fqname specified." << std::endl;
282         exit(1);
283     }
284 
285     Coordinator coordinator;
286     std::string outputPath;
287     std::string fileHeader;
288     bool forceConvertOldInterfaces = false;
289     coordinator.parseOptions(argc, argv, "fho:l:e", [&](int res, char* arg) {
290         switch (res) {
291             case 'o': {
292                 if (!outputPath.empty()) {
293                     fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
294                     exit(1);
295                 }
296                 outputPath = arg;
297                 break;
298             }
299             case 'l':
300                 if (!fileHeader.empty()) {
301                     fprintf(stderr, "ERROR: -l <header file> can only be specified once.\n");
302                     exit(1);
303                 }
304                 fileHeader = arg;
305                 break;
306             case 'f':
307                 forceConvertOldInterfaces = true;
308                 break;
309             case 'e':
310                 AidlHelper::setExpandExtended(true);
311                 break;
312             case 'h':
313             case '?':
314             default: {
315                 usage(me);
316                 exit(1);
317                 break;
318             }
319         }
320     });
321 
322     if (!outputPath.empty() && outputPath.back() != '/') {
323         outputPath += "/";
324     }
325     coordinator.setOutputPath(outputPath);
326     AidlHelper::setFileHeader(fileHeader);
327 
328     argc -= optind;
329     argv += optind;
330 
331     if (argc == 0) {
332         usage(me);
333         std::cerr << "ERROR: no fqname specified." << std::endl;
334         exit(1);
335     }
336 
337     if (argc > 1) {
338         usage(me);
339         std::cerr << "ERROR: only one fqname can be specified." << std::endl;
340         exit(1);
341     }
342 
343     const char* arg = argv[0];
344 
345     FQName fqName;
346     if (!FQName::parse(arg, &fqName)) {
347         std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "." << std::endl;
348         exit(1);
349     }
350 
351     if (fqName.isFullyQualified()) {
352         std::cerr << "ERROR: hidl2aidl only supports converting an entire package, try "
353                      "converting "
354                   << fqName.getPackageAndVersion().string() << " instead." << std::endl;
355         exit(1);
356     }
357 
358     if (!packageExists(coordinator, fqName)) {
359         std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl;
360         exit(1);
361     }
362 
363     if (!forceConvertOldInterfaces) {
364         const FQName highestFqName = getHighestExistingFqName(coordinator, fqName);
365         if (fqName != highestFqName) {
366             std::cerr << "ERROR: A newer minor version of " << fqName.string() << " exists ("
367                       << highestFqName.string()
368                       << "). In general, prefer to convert that instead. If you really mean to "
369                          "use an old minor version use '-f'."
370                       << std::endl;
371             exit(1);
372         }
373     }
374 
375     // This is the list of all targets which should be converted
376     std::vector<FQName> targets;
377     for (FQName version = getLowestExistingFqName(coordinator, fqName);
378          version.getPackageMinorVersion() <= fqName.getPackageMinorVersion();
379          version = version.upRev()) {
380         std::vector<FQName> newTargets;
381         status_t err = coordinator.appendPackageInterfacesToVector(version, &newTargets);
382         if (err != OK) exit(1);
383 
384         targets.insert(targets.end(), newTargets.begin(), newTargets.end());
385     }
386 
387     // targets should not contain duplicates since appendPackageInterfaces is only called once
388     // per version. now remove all the elements that are not the "newest"
389     const auto& newEnd =
390             std::remove_if(targets.begin(), targets.end(), [&](const FQName& fqName) -> bool {
391                 if (fqName.name() == "types") return false;
392 
393                 return getLatestMinorVersionFQNameFromList(fqName, targets) != fqName;
394             });
395     targets.erase(newEnd, targets.end());
396 
397     // Set up AIDL conversion log
398     Formatter err =
399             coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "conversion.log");
400     err << "Notes relating to hidl2aidl conversion of " << fqName.string() << " to "
401         << AidlHelper::getAidlPackage(fqName) << " (if any) follow:\n";
402     AidlHelper::setNotes(&err);
403 
404     // Gather all the types and interfaces
405     std::set<const NamedType*> namedTypesInPackage;
406     for (const FQName& target : targets) {
407 
408         AST* ast = parse(coordinator, target);
409         CHECK(ast);
410 
411         const Interface* iface = ast->getInterface();
412         if (iface) {
413             namedTypesInPackage.insert(iface);
414 
415             // Get all of the types defined in the interface chain(includes self)
416             for (const Interface* interface : iface->typeChain()) {
417                 if (!AidlHelper::shouldBeExpanded(iface->fqName(), interface->fqName())) {
418                     break;
419                 }
420                 getSubTypes(*interface, &namedTypesInPackage);
421             }
422         } else {
423             getSubTypes(ast->getRootScope(), &namedTypesInPackage);
424         }
425     }
426 
427     // Remove all of the older versions of types and keep the latest
428     for (auto it = namedTypesInPackage.begin(); it != namedTypesInPackage.end();) {
429         if (getLatestMinorVersionNamedTypeFromList((*it)->fqName(), namedTypesInPackage) !=
430             (*it)->fqName()) {
431             it = namedTypesInPackage.erase(it);
432         } else {
433             it++;
434         }
435     }
436 
437     // Process and flatten all of the types. Many types include fields of older
438     // versions of that type.
439     // This step recursively finds all of those fields and adds their fields to
440     // the most recent top level type.
441     std::map<const NamedType*, const ProcessedCompoundType> processedTypesInPackage;
442     for (const auto& namedType : namedTypesInPackage) {
443         if (namedType->isCompoundType()) {
444             ProcessedCompoundType processed;
445             AidlHelper::processCompoundType(static_cast<const CompoundType&>(*namedType),
446                                             &processed, std::string());
447             processedTypesInPackage.insert(
448                     std::pair<const NamedType*, const ProcessedCompoundType>(namedType, processed));
449         }
450     }
451 
452     Formatter buildFile =
453             coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "Android.bp");
454     emitBuildFile(buildFile, fqName, targets, !processedTypesInPackage.empty());
455     AidlHelper::emitTranslation(coordinator, fqName, namedTypesInPackage, processedTypesInPackage);
456 
457     // Emit all types and interfaces
458     // The interfaces and types are still be further manipulated inside
459     // emitAidl. The interfaces are consolidating methods from their typechains
460     // and the composite types are being flattened.
461     for (const auto& namedType : namedTypesInPackage) {
462         // Nested types do not get their own files
463         if (namedType->fqName().names().size() > 1) continue;
464         Formatter out =
465                 AidlHelper::getFileWithHeader(*namedType, coordinator, processedTypesInPackage);
466         AidlHelper::emitAidl(*namedType, out, processedTypesInPackage);
467     }
468 
469     err << "END OF LOG\n";
470 
471     return 0;
472 }
473