/* * 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 "Coordinator.h" #include "DocComment.h" using namespace android; static void usage(const char* me) { Formatter out(stderr); out << "Usage: " << me << " [-o ] "; 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 << "-o : Location to output files.\n"; out << "-h: Prints this menu.\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::vector& 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; } 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; } // 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; coordinator.parseOptions(argc, argv, "ho:", [&](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 'h': case '?': default: { usage(me); exit(1); break; } } }); if (!outputPath.empty() && outputPath.back() != '/') { outputPath += "/"; } coordinator.setOutputPath(outputPath); argc -= optind; argv += optind; if (argc == 0) { usage(me); std::cerr << "ERROR: no fqname specified." << std::endl; exit(1); } for (int i = 0; i < argc; ++i) { const char* arg = argv[i]; FQName fqName; if (!FQName::parse(arg, &fqName)) { std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "." << std::endl; exit(1); } if (!packageExists(coordinator, fqName)) { std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl; exit(1); } FQName currentFqName(fqName); while (currentFqName.getPackageMinorVersion() != 0) { if (!packageExists(coordinator, currentFqName.downRev())) break; currentFqName = currentFqName.downRev(); } std::vector targets; while (packageExists(coordinator, currentFqName)) { std::vector newTargets; status_t err = coordinator.appendPackageInterfacesToVector(currentFqName, &newTargets); if (err != OK) break; targets.insert(targets.end(), newTargets.begin(), newTargets.end()); currentFqName = currentFqName.upRev(); } // 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()); if (fqName.isFullyQualified()) { // Ensure that this fqName exists in the list. // If not then there is a more recent version if (std::find(targets.begin(), targets.end(), fqName) == targets.end()) { // Not found. Error. std::cerr << "ERROR: A newer minor version of " << fqName.string() << " exists. Compile that instead." << std::endl; exit(1); } else { targets.clear(); targets.push_back(fqName); } } // Set up AIDL conversion log std::string aidlPackage = AidlHelper::getAidlPackage(fqName); std::string aidlName = AidlHelper::getAidlName(fqName); Formatter err = coordinator.getFormatter( fqName, Coordinator::Location::DIRECT, base::Join(base::Split(aidlPackage, "."), "/") + "/" + (aidlName.empty() ? "" : (aidlName + "-")) + "conversion.log"); AidlHelper::setNotes(&err); std::vector namedTypesInPackage; for (const FQName& target : targets) { if (target.name() != "types") continue; AST* ast = parse(coordinator, target); CHECK(!ast->isInterface()); std::vector types = ast->getRootScope().getSortedDefinedTypes(); namedTypesInPackage.insert(namedTypesInPackage.end(), types.begin(), types.end()); } const auto& endNamedTypes = std::remove_if( namedTypesInPackage.begin(), namedTypesInPackage.end(), [&](const NamedType* namedType) -> bool { return getLatestMinorVersionNamedTypeFromList( namedType->fqName(), namedTypesInPackage) != namedType->fqName(); }); namedTypesInPackage.erase(endNamedTypes, namedTypesInPackage.end()); for (const NamedType* namedType : namedTypesInPackage) { AidlHelper::emitAidl(*namedType, coordinator); } for (const FQName& target : targets) { if (target.name() == "types") continue; AST* ast = parse(coordinator, target); const Interface* iface = ast->getInterface(); CHECK(iface); AidlHelper::emitAidl(*iface, coordinator); } } return 0; }