/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "AST.h" #include "AidlHelper.h" #include "CompoundType.h" #include "Coordinator.h" #include "DocComment.h" #include "Interface.h" using namespace android; static void usage(const char* me) { Formatter out(stderr); out << "Usage: " << me << " [-fh] [-o ] [-l
] "; Coordinator::emitOptionsUsageString(out); out << " FQNAME\n\n"; out << "Converts FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)? to an aidl " "equivalent.\n\n"; out.indent(); out.indent(); out << "-f: Force hidl2aidl to convert older packages\n"; out << "-e: Used for expanding extensions and types from other packages\n"; out << "-h: Prints this menu.\n"; out << "-o : Location to output files.\n"; out << "-l
: File containing a header to prepend to generated files.\n"; Coordinator::emitOptionsDetailString(out); out.unindent(); out.unindent(); } static const FQName& getNewerFQName(const FQName& lhs, const FQName& rhs) { CHECK(lhs.package() == rhs.package()); CHECK(lhs.name() == rhs.name()); if (lhs.getPackageMajorVersion() > rhs.getPackageMajorVersion()) return lhs; if (lhs.getPackageMajorVersion() < rhs.getPackageMajorVersion()) return rhs; if (lhs.getPackageMinorVersion() > rhs.getPackageMinorVersion()) return lhs; return rhs; } // If similar FQName is not found, the same one is returned static FQName getLatestMinorVersionFQNameFromList(const FQName& fqName, const std::vector& list) { FQName currentCandidate = fqName; bool found = false; for (const FQName& current : list) { if (current.package() == currentCandidate.package() && current.name() == currentCandidate.name() && current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) { // Prioritize elements in the list over the provided fqName currentCandidate = found ? getNewerFQName(current, currentCandidate) : current; found = true; } } return currentCandidate; } static FQName getLatestMinorVersionNamedTypeFromList(const FQName& fqName, const std::set& list) { FQName currentCandidate = fqName; bool found = false; for (const NamedType* currentNamedType : list) { const FQName& current = currentNamedType->fqName(); if (current.package() == currentCandidate.package() && current.name() == currentCandidate.name() && current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) { // Prioritize elements in the list over the provided fqName currentCandidate = found ? getNewerFQName(current, currentCandidate) : current; found = true; } } return currentCandidate; } static bool packageExists(const Coordinator& coordinator, const FQName& fqName) { bool result; status_t err = coordinator.packageExists(fqName, &result); if (err != OK) { std::cerr << "Error trying to find package " << fqName.string() << std::endl; exit(1); } return result; } // assuming fqName exists, find oldest version which does exist // e.g. android.hardware.foo@1.7 -> android.hardware.foo@1.1 (if foo@1.0 doesn't // exist) static FQName getLowestExistingFqName(const Coordinator& coordinator, const FQName& fqName) { FQName lowest(fqName); while (lowest.getPackageMinorVersion() != 0) { if (!packageExists(coordinator, lowest.downRev())) break; lowest = lowest.downRev(); } return lowest; } // assuming fqName exists, find newest version which does exist // e.g. android.hardware.foo@1.1 -> android.hardware.foo@1.7 if that's the // newest static FQName getHighestExistingFqName(const Coordinator& coordinator, const FQName& fqName) { FQName highest(fqName); while (packageExists(coordinator, highest.upRev())) { highest = highest.upRev(); } return highest; } static AST* parse(const Coordinator& coordinator, const FQName& target) { AST* ast = coordinator.parse(target); if (ast == nullptr) { std::cerr << "ERROR: Could not parse " << target.name() << ". Aborting." << std::endl; exit(1); } if (!ast->getUnhandledComments().empty()) { AidlHelper::notes() << "Unhandled comments from " << target.string() << " follow. Consider using hidl-lint to locate these and fixup as many " << "as possible.\n"; for (const DocComment* docComment : ast->getUnhandledComments()) { docComment->emit(AidlHelper::notes()); } AidlHelper::notes() << "\n"; } return ast; } static void getSubTypes(const NamedType& namedType, std::set* types) { if (namedType.isScope()) { const Scope& compoundType = static_cast(namedType); for (const NamedType* subType : compoundType.getSubTypes()) { types->insert(subType); getSubTypes(*subType, types); } } } static void emitAidlSharedLibs(Formatter& out, FQName fqName, AidlBackend backend) { if (backend == AidlBackend::NDK) { out << " \"libbinder_ndk\",\n"; out << " \"libhidlbase\",\n"; out << " \"" << AidlHelper::getAidlPackage(fqName) << "-V1-ndk_platform\",\n"; } else if (backend == AidlBackend::CPP) { out << " \"libbinder\",\n"; out << " \"libhidlbase\",\n"; out << " \"" << AidlHelper::getAidlPackage(fqName) << "-V1-cpp\",\n"; out << " \"libutils\",\n"; } else { out << " \"" << AidlHelper::getAidlPackage(fqName) << "-V1-java\",\n"; } } static void emitHidlSharedLibs(Formatter& out, std::vector& targets, AidlBackend backend) { std::set uniquePackages; for (const auto& target : targets) { if (backend == AidlBackend::JAVA) { uniquePackages.insert( android::base::StringReplace(target.getPackageAndVersion().string(), "@", "-V", false /* all */) + "-java"); } else { uniquePackages.insert(target.getPackageAndVersion().string()); } } for (const auto& package : uniquePackages) { out << " \"" << package << "\",\n"; } } static std::string aidlTranslateLibraryName(FQName fqName, AidlBackend backend) { std::string postfix; if (backend == AidlBackend::NDK) { postfix = "-ndk"; } else if (backend == AidlBackend::CPP) { postfix = "-cpp"; } else { postfix = "-java"; } return AidlHelper::getAidlPackage(fqName) + "-translate" + postfix; } static void emitBuildFile(Formatter& out, const FQName& fqName, std::vector& targets, bool needsTranslation) { out << "// This is the expected build file, but it may not be right in all cases\n"; out << "\n"; out << "aidl_interface {\n"; out << " name: \"" << AidlHelper::getAidlPackage(fqName) << "\",\n"; out << " vendor_available: true,\n"; out << " srcs: [\"" << AidlHelper::getAidlPackagePath(fqName) << "/*.aidl\"],\n"; out << " stability: \"vintf\",\n"; out << " backend: {\n"; out << " cpp: {\n"; out << " // FIXME should this be disabled?\n"; out << " // prefer NDK backend which can be used anywhere\n"; out << " enabled: true,\n"; out << " },\n"; out << " java: {\n"; out << " sdk_version: \"module_current\",\n"; out << " },\n"; out << " ndk: {\n"; out << " vndk: {\n"; out << " enabled: true,\n"; out << " },\n"; out << " },\n"; out << " },\n"; out << "}\n\n"; if (!needsTranslation) return; for (auto backend : {AidlBackend::CPP, AidlBackend::NDK}) { out << "cc_library {\n"; out << " name: \"" << aidlTranslateLibraryName(fqName, backend) << +"\",\n"; if (backend == AidlBackend::NDK) { out << " vendor_available: true,\n"; } out << " srcs: [\"" << AidlHelper::translateSourceFile(fqName, backend) + "\"],\n"; out << " shared_libs: [\n"; emitAidlSharedLibs(out, fqName, backend); emitHidlSharedLibs(out, targets, backend); out << " ],\n"; out << " export_include_dirs: [\"include\"],\n"; out << "}\n\n"; } out << "java_library {\n"; out << " name: \"" << aidlTranslateLibraryName(fqName, AidlBackend::JAVA) << +"\",\n"; out << " srcs: [\"" << AidlHelper::translateSourceFile(fqName, AidlBackend::JAVA) + "\"],\n"; out << " libs: [\n"; emitAidlSharedLibs(out, fqName, AidlBackend::JAVA); emitHidlSharedLibs(out, targets, AidlBackend::JAVA); out << " ],\n"; out << " sdk_version: \"module_current\",\n"; out << "}\n\n"; } // hidl is intentionally leaky. Turn off LeakSanitizer by default. extern "C" const char* __asan_default_options() { return "detect_leaks=0"; } int main(int argc, char** argv) { const char* me = argv[0]; if (argc == 1) { usage(me); std::cerr << "ERROR: no fqname specified." << std::endl; exit(1); } Coordinator coordinator; std::string outputPath; std::string fileHeader; bool forceConvertOldInterfaces = false; coordinator.parseOptions(argc, argv, "fho:l:e", [&](int res, char* arg) { switch (res) { case 'o': { if (!outputPath.empty()) { fprintf(stderr, "ERROR: -o can only be specified once.\n"); exit(1); } outputPath = arg; break; } case 'l': if (!fileHeader.empty()) { fprintf(stderr, "ERROR: -l
can only be specified once.\n"); exit(1); } fileHeader = arg; break; case 'f': forceConvertOldInterfaces = true; break; case 'e': AidlHelper::setExpandExtended(true); break; case 'h': case '?': default: { usage(me); exit(1); break; } } }); if (!outputPath.empty() && outputPath.back() != '/') { outputPath += "/"; } coordinator.setOutputPath(outputPath); AidlHelper::setFileHeader(fileHeader); argc -= optind; argv += optind; if (argc == 0) { usage(me); std::cerr << "ERROR: no fqname specified." << std::endl; exit(1); } if (argc > 1) { usage(me); std::cerr << "ERROR: only one fqname can be specified." << std::endl; exit(1); } const char* arg = argv[0]; FQName fqName; if (!FQName::parse(arg, &fqName)) { std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "." << std::endl; exit(1); } if (fqName.isFullyQualified()) { std::cerr << "ERROR: hidl2aidl only supports converting an entire package, try " "converting " << fqName.getPackageAndVersion().string() << " instead." << std::endl; exit(1); } if (!packageExists(coordinator, fqName)) { std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl; exit(1); } if (!forceConvertOldInterfaces) { const FQName highestFqName = getHighestExistingFqName(coordinator, fqName); if (fqName != highestFqName) { std::cerr << "ERROR: A newer minor version of " << fqName.string() << " exists (" << highestFqName.string() << "). In general, prefer to convert that instead. If you really mean to " "use an old minor version use '-f'." << std::endl; exit(1); } } // This is the list of all types which should be converted std::vector targets; for (FQName version = getLowestExistingFqName(coordinator, fqName); version.getPackageMinorVersion() <= fqName.getPackageMinorVersion(); version = version.upRev()) { std::vector newTargets; status_t err = coordinator.appendPackageInterfacesToVector(version, &newTargets); if (err != OK) exit(1); targets.insert(targets.end(), newTargets.begin(), newTargets.end()); } // targets should not contain duplicates since appendPackageInterfaces is only called once // per version. now remove all the elements that are not the "newest" const auto& newEnd = std::remove_if(targets.begin(), targets.end(), [&](const FQName& fqName) -> bool { if (fqName.name() == "types") return false; return getLatestMinorVersionFQNameFromList(fqName, targets) != fqName; }); targets.erase(newEnd, targets.end()); // Set up AIDL conversion log Formatter err = coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "conversion.log"); err << "Notes relating to hidl2aidl conversion of " << fqName.string() << " to " << AidlHelper::getAidlPackage(fqName) << " (if any) follow:\n"; AidlHelper::setNotes(&err); // Gather all the types and interfaces std::set namedTypesInPackage; for (const FQName& target : targets) { AST* ast = parse(coordinator, target); CHECK(ast); const Interface* iface = ast->getInterface(); if (iface) { namedTypesInPackage.insert(iface); // Get all of the types defined in the interface chain(includes self) for (const Interface* interface : iface->typeChain()) { if (!AidlHelper::shouldBeExpanded(iface->fqName(), interface->fqName())) { break; } getSubTypes(*interface, &namedTypesInPackage); } } else { getSubTypes(ast->getRootScope(), &namedTypesInPackage); } } // Remove all of the older versions of types and keep the latest for (auto it = namedTypesInPackage.begin(); it != namedTypesInPackage.end();) { if (getLatestMinorVersionNamedTypeFromList((*it)->fqName(), namedTypesInPackage) != (*it)->fqName()) { it = namedTypesInPackage.erase(it); } else { it++; } } // Process and flatten all of the types. Many types include fields of older // versions of that type. // This step recursively finds all of those fields and adds their fields to // the most recent top level type. std::map processedTypesInPackage; for (const auto& namedType : namedTypesInPackage) { if (namedType->isCompoundType()) { ProcessedCompoundType processed; AidlHelper::processCompoundType(static_cast(*namedType), &processed, std::string()); processedTypesInPackage.insert( std::pair(namedType, processed)); } } Formatter buildFile = coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "Android.bp"); emitBuildFile(buildFile, fqName, targets, !processedTypesInPackage.empty()); AidlHelper::emitTranslation(coordinator, fqName, namedTypesInPackage, processedTypesInPackage); // Emit all types and interfaces // The interfaces and types are still be further manipulated inside // emitAidl. The interfaces are consolidating methods from their typechains // and the composite types are being flattened. for (const auto& namedType : namedTypesInPackage) { AidlHelper::emitAidl(*namedType, coordinator, processedTypesInPackage); } err << "END OF LOG\n"; return 0; }