|  | /* | 
|  | * Copyright (C) 2016 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 "AST.h" | 
|  | #include "Coordinator.h" | 
|  | #include "Interface.h" | 
|  | #include "Scope.h" | 
|  |  | 
|  | #include <android-base/logging.h> | 
|  | #include <hidl-hash/Hash.h> | 
|  | #include <hidl-util/FQName.h> | 
|  | #include <hidl-util/Formatter.h> | 
|  | #include <hidl-util/StringHelper.h> | 
|  | #include <stdio.h> | 
|  | #include <sys/stat.h> | 
|  | #include <unistd.h> | 
|  | #include <iostream> | 
|  | #include <set> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | using namespace android; | 
|  |  | 
|  | enum class OutputMode { | 
|  | NEEDS_DIR,   // -o output option expects a directory | 
|  | NEEDS_FILE,  // -o output option expects a file | 
|  | NEEDS_SRC,   // for changes inside the source tree itself | 
|  | NOT_NEEDED   // does not create files | 
|  | }; | 
|  |  | 
|  | enum class GenerationGranularity { | 
|  | PER_PACKAGE,  // Files generated for each package | 
|  | PER_FILE,     // Files generated for each hal file | 
|  | PER_TYPE,     // Files generated for each hal file + each type in HAL files | 
|  | }; | 
|  |  | 
|  | // Represents a file that is generated by an -L option for an FQName | 
|  | struct FileGenerator { | 
|  | using ShouldGenerateFunction = std::function<bool(const FQName& fqName)>; | 
|  | using FileNameForFQName = std::function<std::string(const FQName& fqName)>; | 
|  | using GenerationFunction = std::function<status_t(Formatter& out, const FQName& fqName, | 
|  | const Coordinator* coordinator)>; | 
|  |  | 
|  | ShouldGenerateFunction mShouldGenerateForFqName;  // If generate function applies to this target | 
|  | FileNameForFQName mFileNameForFqName;             // Target -> filename | 
|  | GenerationFunction mGenerationFunction;           // Function to generate output for file | 
|  |  | 
|  | std::string getFileName(const FQName& fqName) const { | 
|  | return mFileNameForFqName ? mFileNameForFqName(fqName) : ""; | 
|  | } | 
|  |  | 
|  | status_t getOutputFile(const FQName& fqName, const Coordinator* coordinator, | 
|  | Coordinator::Location location, std::string* file) const { | 
|  | if (!mShouldGenerateForFqName(fqName)) { | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | return coordinator->getFilepath(fqName, location, getFileName(fqName), file); | 
|  | } | 
|  |  | 
|  | status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator, | 
|  | Coordinator::Location location, | 
|  | std::vector<std::string>* outputFiles) const { | 
|  | if (location == Coordinator::Location::STANDARD_OUT) { | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | if (mShouldGenerateForFqName(fqName)) { | 
|  | std::string fileName; | 
|  | status_t err = getOutputFile(fqName, coordinator, location, &fileName); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | if (!fileName.empty()) { | 
|  | outputFiles->push_back(fileName); | 
|  | } | 
|  | } | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | status_t generate(const FQName& fqName, const Coordinator* coordinator, | 
|  | Coordinator::Location location) const { | 
|  | CHECK(mShouldGenerateForFqName != nullptr); | 
|  | CHECK(mGenerationFunction != nullptr); | 
|  |  | 
|  | if (!mShouldGenerateForFqName(fqName)) { | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | Formatter out = coordinator->getFormatter(fqName, location, getFileName(fqName)); | 
|  | if (!out.isValid()) { | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | return mGenerationFunction(out, fqName, coordinator); | 
|  | } | 
|  |  | 
|  | // Helper methods for filling out this struct | 
|  | static bool generateForTypes(const FQName& fqName) { | 
|  | const auto names = fqName.names(); | 
|  | return names.size() > 0 && names[0] == "types"; | 
|  | } | 
|  | static bool generateForInterfaces(const FQName& fqName) { return !generateForTypes(fqName); } | 
|  | static bool alwaysGenerate(const FQName&) { return true; } | 
|  | }; | 
|  |  | 
|  | // Represents a -L option, takes a fqName and generates files | 
|  | struct OutputHandler { | 
|  | using ValidationFunction = std::function<bool( | 
|  | const FQName& fqName, const Coordinator* coordinator, const std::string& language)>; | 
|  |  | 
|  | std::string mKey;                 // -L in Android.bp | 
|  | std::string mDescription;         // for display in help menu | 
|  | OutputMode mOutputMode;           // how this option interacts with -o | 
|  | Coordinator::Location mLocation;  // how to compute location relative to the output directory | 
|  | GenerationGranularity mGenerationGranularity;   // what to run generate function on | 
|  | ValidationFunction mValidate;                   // if a given fqName is allowed for this option | 
|  | std::vector<FileGenerator> mGenerateFunctions;  // run for each target at this granularity | 
|  |  | 
|  | const std::string& name() const { return mKey; } | 
|  | const std::string& description() const { return mDescription; } | 
|  |  | 
|  | status_t generate(const FQName& fqName, const Coordinator* coordinator) const; | 
|  | status_t validate(const FQName& fqName, const Coordinator* coordinator, | 
|  | const std::string& language) const { | 
|  | return mValidate(fqName, coordinator, language); | 
|  | } | 
|  |  | 
|  | status_t writeDepFile(const FQName& fqName, const Coordinator* coordinator) const; | 
|  |  | 
|  | private: | 
|  | status_t appendTargets(const FQName& fqName, const Coordinator* coordinator, | 
|  | std::vector<FQName>* targets) const; | 
|  | status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator, | 
|  | std::vector<std::string>* outputFiles) const; | 
|  | }; | 
|  |  | 
|  | // Helper method for GenerationGranularity::PER_TYPE | 
|  | // IFoo -> IFoo, types.hal (containing Bar, Baz) -> types.Bar, types.Baz | 
|  | static status_t appendPerTypeTargets(const FQName& fqName, const Coordinator* coordinator, | 
|  | std::vector<FQName>* exportedPackageInterfaces) { | 
|  | CHECK(fqName.isFullyQualified()); | 
|  | if (fqName.name() != "types") { | 
|  | exportedPackageInterfaces->push_back(fqName); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | AST* typesAST = coordinator->parse(fqName); | 
|  | if (typesAST == nullptr) { | 
|  | fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | std::vector<NamedType*> rootTypes = typesAST->getRootScope()->getSubTypes(); | 
|  | for (const NamedType* rootType : rootTypes) { | 
|  | if (rootType->isTypeDef()) continue; | 
|  |  | 
|  | FQName rootTypeName(fqName.package(), fqName.version(), "types." + rootType->localName()); | 
|  | exportedPackageInterfaces->push_back(rootTypeName); | 
|  | } | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | status_t OutputHandler::appendTargets(const FQName& fqName, const Coordinator* coordinator, | 
|  | std::vector<FQName>* targets) const { | 
|  | switch (mGenerationGranularity) { | 
|  | case GenerationGranularity::PER_PACKAGE: { | 
|  | targets->push_back(fqName.getPackageAndVersion()); | 
|  | } break; | 
|  | case GenerationGranularity::PER_FILE: { | 
|  | if (fqName.isFullyQualified()) { | 
|  | targets->push_back(fqName); | 
|  | break; | 
|  | } | 
|  | status_t err = coordinator->appendPackageInterfacesToVector(fqName, targets); | 
|  | if (err != OK) return err; | 
|  | } break; | 
|  | case GenerationGranularity::PER_TYPE: { | 
|  | if (fqName.isFullyQualified()) { | 
|  | status_t err = appendPerTypeTargets(fqName, coordinator, targets); | 
|  | if (err != OK) return err; | 
|  | } | 
|  |  | 
|  | std::vector<FQName> packageInterfaces; | 
|  | status_t err = coordinator->appendPackageInterfacesToVector(fqName, &packageInterfaces); | 
|  | if (err != OK) return err; | 
|  | for (const FQName& packageInterface : packageInterfaces) { | 
|  | err = appendPerTypeTargets(packageInterface, coordinator, targets); | 
|  | if (err != OK) return err; | 
|  | } | 
|  | } break; | 
|  | default: | 
|  | CHECK(!"Should be here"); | 
|  | } | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | status_t OutputHandler::generate(const FQName& fqName, const Coordinator* coordinator) const { | 
|  | std::vector<FQName> targets; | 
|  | status_t err = appendTargets(fqName, coordinator, &targets); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | for (const FQName& fqName : targets) { | 
|  | for (const FileGenerator& file : mGenerateFunctions) { | 
|  | status_t err = file.generate(fqName, coordinator, mLocation); | 
|  | if (err != OK) return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | status_t OutputHandler::appendOutputFiles(const FQName& fqName, const Coordinator* coordinator, | 
|  | std::vector<std::string>* outputFiles) const { | 
|  | std::vector<FQName> targets; | 
|  | status_t err = appendTargets(fqName, coordinator, &targets); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | for (const FQName& fqName : targets) { | 
|  | for (const FileGenerator& file : mGenerateFunctions) { | 
|  | err = file.appendOutputFiles(fqName, coordinator, mLocation, outputFiles); | 
|  | if (err != OK) return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | status_t OutputHandler::writeDepFile(const FQName& fqName, const Coordinator* coordinator) const { | 
|  | std::vector<std::string> outputFiles; | 
|  | status_t err = appendOutputFiles(fqName, coordinator, &outputFiles); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | // No need for dep files | 
|  | if (outputFiles.empty()) { | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | // Depfiles in Android for genrules should be for the 'main file'. Because hidl-gen doesn't have | 
|  | // a main file for most targets, we are just outputting a depfile for one single file only. | 
|  | const std::string forFile = outputFiles[0]; | 
|  |  | 
|  | return coordinator->writeDepFile(forFile); | 
|  | } | 
|  |  | 
|  | // Use an AST function as a OutputHandler GenerationFunction | 
|  | static FileGenerator::GenerationFunction astGenerationFunction(void (AST::*generate)(Formatter&) | 
|  | const = nullptr) { | 
|  | return [generate](Formatter& out, const FQName& fqName, | 
|  | const Coordinator* coordinator) -> status_t { | 
|  | AST* ast = coordinator->parse(fqName); | 
|  | if (ast == nullptr) { | 
|  | fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | if (generate == nullptr) return OK;  // just parsing AST | 
|  | (ast->*generate)(out); | 
|  |  | 
|  | return OK; | 
|  | }; | 
|  | } | 
|  |  | 
|  | // Common pattern: single file for package or standard out | 
|  | static FileGenerator singleFileGenerator( | 
|  | const std::string& fileName, const FileGenerator::GenerationFunction& generationFunction) { | 
|  | return { | 
|  | FileGenerator::alwaysGenerate, [fileName](const FQName&) { return fileName; }, | 
|  | generationFunction, | 
|  | }; | 
|  | } | 
|  |  | 
|  | static status_t generateJavaForPackage(Formatter& out, const FQName& fqName, | 
|  | const Coordinator* coordinator) { | 
|  | AST* ast; | 
|  | std::string limitToType; | 
|  |  | 
|  | // Required for legacy -Lmakefile files | 
|  | if (fqName.name().find("types.") == 0) { | 
|  | limitToType = fqName.name().substr(strlen("types.")); | 
|  |  | 
|  | FQName typesName = fqName.getTypesForPackage(); | 
|  | ast = coordinator->parse(typesName); | 
|  | } else { | 
|  | ast = coordinator->parse(fqName); | 
|  | } | 
|  | if (ast == nullptr) { | 
|  | fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  | ast->generateJava(out, limitToType); | 
|  | return OK; | 
|  | }; | 
|  |  | 
|  | static status_t dumpDefinedButUnreferencedTypeNames(const FQName& packageFQName, | 
|  | const Coordinator* coordinator) { | 
|  | std::vector<FQName> packageInterfaces; | 
|  | status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | std::set<FQName> unreferencedDefinitions; | 
|  | std::set<FQName> unreferencedImports; | 
|  | err = coordinator->addUnreferencedTypes(packageInterfaces, &unreferencedDefinitions, | 
|  | &unreferencedImports); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | for (const auto& fqName : unreferencedDefinitions) { | 
|  | std::cerr | 
|  | << "VERBOSE: DEFINED-BUT-NOT-REFERENCED " | 
|  | << fqName.string() | 
|  | << std::endl; | 
|  | } | 
|  |  | 
|  | for (const auto& fqName : unreferencedImports) { | 
|  | std::cerr | 
|  | << "VERBOSE: IMPORTED-BUT-NOT-REFERENCED " | 
|  | << fqName.string() | 
|  | << std::endl; | 
|  | } | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | static std::string makeLibraryName(const FQName &packageFQName) { | 
|  | return packageFQName.string(); | 
|  | } | 
|  |  | 
|  | static status_t isPackageJavaCompatible(const FQName& packageFQName, const Coordinator* coordinator, | 
|  | bool* compatible) { | 
|  | std::vector<FQName> todo; | 
|  | status_t err = | 
|  | coordinator->appendPackageInterfacesToVector(packageFQName, &todo); | 
|  |  | 
|  | if (err != OK) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | std::set<FQName> seen; | 
|  | for (const auto &iface : todo) { | 
|  | seen.insert(iface); | 
|  | } | 
|  |  | 
|  | // Form the transitive closure of all imported interfaces (and types.hal-s) | 
|  | // If any one of them is not java compatible, this package isn't either. | 
|  | while (!todo.empty()) { | 
|  | const FQName fqName = todo.back(); | 
|  | todo.pop_back(); | 
|  |  | 
|  | AST *ast = coordinator->parse(fqName); | 
|  |  | 
|  | if (ast == nullptr) { | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | if (!ast->isJavaCompatible()) { | 
|  | *compatible = false; | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | std::set<FQName> importedPackages; | 
|  | ast->getImportedPackages(&importedPackages); | 
|  |  | 
|  | for (const auto &package : importedPackages) { | 
|  | std::vector<FQName> packageInterfaces; | 
|  | status_t err = coordinator->appendPackageInterfacesToVector( | 
|  | package, &packageInterfaces); | 
|  |  | 
|  | if (err != OK) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | for (const auto &iface : packageInterfaces) { | 
|  | if (seen.find(iface) != seen.end()) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | todo.push_back(iface); | 
|  | seen.insert(iface); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | *compatible = true; | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | static bool packageNeedsJavaCode( | 
|  | const std::vector<FQName> &packageInterfaces, AST *typesAST) { | 
|  | if (packageInterfaces.size() == 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // If there is more than just a types.hal file to this package we'll | 
|  | // definitely need to generate Java code. | 
|  | if (packageInterfaces.size() > 1 | 
|  | || packageInterfaces[0].name() != "types") { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | CHECK(typesAST != nullptr); | 
|  |  | 
|  | // We'll have to generate Java code if types.hal contains any non-typedef | 
|  | // type declarations. | 
|  |  | 
|  | Scope* rootScope = typesAST->getRootScope(); | 
|  | std::vector<NamedType *> subTypes = rootScope->getSubTypes(); | 
|  |  | 
|  | for (const auto &subType : subTypes) { | 
|  | if (!subType->isTypeDef()) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool validateIsPackage(const FQName& fqName, const Coordinator*, | 
|  | const std::string& /* language */) { | 
|  | if (fqName.package().empty()) { | 
|  | fprintf(stderr, "ERROR: Expecting package name\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (fqName.version().empty()) { | 
|  | fprintf(stderr, "ERROR: Expecting package version\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!fqName.name().empty()) { | 
|  | fprintf(stderr, | 
|  | "ERROR: Expecting only package name and version.\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool isHidlTransportPackage(const FQName& fqName) { | 
|  | return fqName.package() == gIBaseFqName.package() || | 
|  | fqName.package() == gIManagerFqName.package(); | 
|  | } | 
|  |  | 
|  | bool isSystemProcessSupportedPackage(const FQName& fqName) { | 
|  | // Technically, so is hidl IBase + IServiceManager, but | 
|  | // these are part of libhidltransport. | 
|  | return fqName.inPackage("android.hardware.graphics.common") || | 
|  | fqName.inPackage("android.hardware.graphics.mapper") || | 
|  | fqName.string() == "android.hardware.renderscript@1.0" || | 
|  | fqName.string() == "android.hidl.memory.token@1.0" || | 
|  | fqName.string() == "android.hidl.memory@1.0" || | 
|  | fqName.string() == "android.hidl.safe_union@1.0"; | 
|  | } | 
|  |  | 
|  | bool isCoreAndroidPackage(const FQName& package) { | 
|  | return package.inPackage("android.hidl") || | 
|  | package.inPackage("android.system") || | 
|  | package.inPackage("android.frameworks") || | 
|  | package.inPackage("android.hardware"); | 
|  | } | 
|  |  | 
|  | // TODO(b/69862859): remove special case | 
|  | status_t isTestPackage(const FQName& fqName, const Coordinator* coordinator, bool* isTestPackage) { | 
|  | const auto fileExists = [](const std::string& file) { | 
|  | struct stat buf; | 
|  | return stat(file.c_str(), &buf) == 0; | 
|  | }; | 
|  |  | 
|  | std::string path; | 
|  | status_t err = coordinator->getFilepath(fqName, Coordinator::Location::PACKAGE_ROOT, | 
|  | ".hidl_for_test", &path); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | const bool exists = fileExists(path); | 
|  |  | 
|  | if (exists) { | 
|  | coordinator->onFileAccess(path, "r"); | 
|  | } | 
|  |  | 
|  | *isTestPackage = exists; | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | static status_t generateAdapterMainSource(Formatter& out, const FQName& packageFQName, | 
|  | const Coordinator* coordinator) { | 
|  | std::vector<FQName> packageInterfaces; | 
|  | status_t err = | 
|  | coordinator->appendPackageInterfacesToVector(packageFQName, | 
|  | &packageInterfaces); | 
|  | if (err != OK) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | out << "#include <hidladapter/HidlBinderAdapter.h>\n"; | 
|  |  | 
|  | for (auto &interface : packageInterfaces) { | 
|  | if (interface.name() == "types") { | 
|  | continue; | 
|  | } | 
|  | AST::generateCppPackageInclude(out, interface, interface.getInterfaceAdapterName()); | 
|  | } | 
|  |  | 
|  | out << "int main(int argc, char** argv) "; | 
|  | out.block([&] { | 
|  | out << "return ::android::hardware::adapterMain<\n"; | 
|  | out.indent(); | 
|  | for (auto &interface : packageInterfaces) { | 
|  | if (interface.name() == "types") { | 
|  | continue; | 
|  | } | 
|  | out << interface.getInterfaceAdapterFqName().cppName(); | 
|  |  | 
|  | if (&interface != &packageInterfaces.back()) { | 
|  | out << ",\n"; | 
|  | } | 
|  | } | 
|  | out << ">(\"" << packageFQName.string() << "\", argc, argv);\n"; | 
|  | out.unindent(); | 
|  | }).endl(); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | static status_t generateAndroidBpForPackage(Formatter& out, const FQName& packageFQName, | 
|  | const Coordinator* coordinator) { | 
|  | CHECK(!packageFQName.isFullyQualified() && packageFQName.name().empty()); | 
|  |  | 
|  | std::vector<FQName> packageInterfaces; | 
|  |  | 
|  | status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces); | 
|  |  | 
|  | if (err != OK) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | std::set<FQName> importedPackagesHierarchy; | 
|  | std::vector<const Type *> exportedTypes; | 
|  | AST* typesAST = nullptr; | 
|  |  | 
|  | for (const auto& fqName : packageInterfaces) { | 
|  | AST* ast = coordinator->parse(fqName); | 
|  |  | 
|  | if (ast == nullptr) { | 
|  | fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); | 
|  |  | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | if (fqName.name() == "types") { | 
|  | typesAST = ast; | 
|  | } | 
|  |  | 
|  | ast->getImportedPackagesHierarchy(&importedPackagesHierarchy); | 
|  | ast->appendToExportedTypesVector(&exportedTypes); | 
|  | } | 
|  |  | 
|  | bool needsJavaCode = packageNeedsJavaCode(packageInterfaces, typesAST); | 
|  |  | 
|  | bool genJavaConstants = needsJavaCode && !exportedTypes.empty(); | 
|  |  | 
|  | bool isJavaCompatible; | 
|  | err = isPackageJavaCompatible(packageFQName, coordinator, &isJavaCompatible); | 
|  | if (err != OK) return err; | 
|  | bool genJavaLibrary = needsJavaCode && isJavaCompatible; | 
|  |  | 
|  | bool generateForTest; | 
|  | err = isTestPackage(packageFQName, coordinator, &generateForTest); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | bool isCoreAndroid = isCoreAndroidPackage(packageFQName); | 
|  |  | 
|  | bool isVndk = !generateForTest && isCoreAndroid; | 
|  | bool isVndkSp = isVndk && isSystemProcessSupportedPackage(packageFQName); | 
|  |  | 
|  | // Currently, all platform-provided interfaces are in the VNDK, so if it isn't in the VNDK, it | 
|  | // is device specific and so should be put in the product partition. | 
|  | bool isProduct = !isCoreAndroid; | 
|  |  | 
|  | std::string packageRoot; | 
|  | err = coordinator->getPackageRoot(packageFQName, &packageRoot); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | out << "// This file is autogenerated by hidl-gen -Landroidbp.\n\n"; | 
|  |  | 
|  | out << "hidl_interface "; | 
|  | out.block([&] { | 
|  | out << "name: \"" << makeLibraryName(packageFQName) << "\",\n"; | 
|  | if (!coordinator->getOwner().empty()) { | 
|  | out << "owner: \"" << coordinator->getOwner() << "\",\n"; | 
|  | } | 
|  | out << "root: \"" << packageRoot << "\",\n"; | 
|  | if (isVndk) { | 
|  | out << "vndk: "; | 
|  | out.block([&]() { | 
|  | out << "enabled: true,\n"; | 
|  | if (isVndkSp) { | 
|  | out << "support_system_process: true,\n"; | 
|  | } | 
|  | }) << ",\n"; | 
|  | } | 
|  | if (isProduct) { | 
|  | out << "product_specific: true,\n"; | 
|  | } | 
|  | (out << "srcs: [\n").indent([&] { | 
|  | for (const auto& fqName : packageInterfaces) { | 
|  | out << "\"" << fqName.name() << ".hal\",\n"; | 
|  | } | 
|  | }) << "],\n"; | 
|  | if (!importedPackagesHierarchy.empty()) { | 
|  | (out << "interfaces: [\n").indent([&] { | 
|  | for (const auto& fqName : importedPackagesHierarchy) { | 
|  | out << "\"" << fqName.string() << "\",\n"; | 
|  | } | 
|  | }) << "],\n"; | 
|  | } | 
|  | // Explicity call this out for developers. | 
|  | out << "gen_java: " << (genJavaLibrary ? "true" : "false") << ",\n"; | 
|  | if (genJavaConstants) { | 
|  | out << "gen_java_constants: true,\n"; | 
|  | } | 
|  | }).endl().endl(); | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | static status_t generateAndroidBpImplForPackage(Formatter& out, const FQName& packageFQName, | 
|  | const Coordinator* coordinator) { | 
|  | const std::string libraryName = makeLibraryName(packageFQName) + "-impl"; | 
|  |  | 
|  | std::vector<FQName> packageInterfaces; | 
|  |  | 
|  | status_t err = | 
|  | coordinator->appendPackageInterfacesToVector(packageFQName, | 
|  | &packageInterfaces); | 
|  |  | 
|  | if (err != OK) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | std::set<FQName> importedPackages; | 
|  |  | 
|  | for (const auto &fqName : packageInterfaces) { | 
|  | AST *ast = coordinator->parse(fqName); | 
|  |  | 
|  | if (ast == nullptr) { | 
|  | fprintf(stderr, | 
|  | "ERROR: Could not parse %s. Aborting.\n", | 
|  | fqName.string().c_str()); | 
|  |  | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | ast->getImportedPackages(&importedPackages); | 
|  | } | 
|  |  | 
|  | out << "// FIXME: your file license if you have one\n\n"; | 
|  | out << "cc_library_shared {\n"; | 
|  | out.indent([&] { | 
|  | out << "// FIXME: this should only be -impl for a passthrough hal.\n" | 
|  | << "// In most cases, to convert this to a binderized implementation, you should:\n" | 
|  | << "// - change '-impl' to '-service' here and make it a cc_binary instead of a\n" | 
|  | << "//   cc_library_shared.\n" | 
|  | << "// - add a *.rc file for this module.\n" | 
|  | << "// - delete HIDL_FETCH_I* functions.\n" | 
|  | << "// - call configureRpcThreadpool and registerAsService on the instance.\n" | 
|  | << "// You may also want to append '-impl/-service' with a specific identifier like\n" | 
|  | << "// '-vendor' or '-<hardware identifier>' etc to distinguish it.\n"; | 
|  | out << "name: \"" << libraryName << "\",\n"; | 
|  | if (!coordinator->getOwner().empty()) { | 
|  | out << "owner: \"" << coordinator->getOwner() << "\",\n"; | 
|  | } | 
|  | out << "relative_install_path: \"hw\",\n"; | 
|  | if (coordinator->getOwner().empty()) { | 
|  | out << "// FIXME: this should be 'vendor: true' for modules that will eventually be\n" | 
|  | "// on AOSP.\n"; | 
|  | } | 
|  | out << "proprietary: true,\n"; | 
|  | out << "srcs: [\n"; | 
|  | out.indent([&] { | 
|  | for (const auto &fqName : packageInterfaces) { | 
|  | if (fqName.name() == "types") { | 
|  | continue; | 
|  | } | 
|  | out << "\"" << fqName.getInterfaceBaseName() << ".cpp\",\n"; | 
|  | } | 
|  | }); | 
|  | out << "],\n" | 
|  | << "shared_libs: [\n"; | 
|  | out.indent([&] { | 
|  | out << "\"libhidlbase\",\n" | 
|  | << "\"libhidltransport\",\n" | 
|  | << "\"libutils\",\n" | 
|  | << "\"" << makeLibraryName(packageFQName) << "\",\n"; | 
|  |  | 
|  | for (const auto &importedPackage : importedPackages) { | 
|  | if (isHidlTransportPackage(importedPackage)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | out << "\"" << makeLibraryName(importedPackage) << "\",\n"; | 
|  | } | 
|  | }); | 
|  | out << "],\n"; | 
|  | }); | 
|  | out << "}\n"; | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | bool validateForSource(const FQName& fqName, const Coordinator* coordinator, | 
|  | const std::string& language) { | 
|  | if (fqName.package().empty()) { | 
|  | fprintf(stderr, "ERROR: Expecting package name\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (fqName.version().empty()) { | 
|  | fprintf(stderr, "ERROR: Expecting package version\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const std::string &name = fqName.name(); | 
|  | if (!name.empty()) { | 
|  | if (name.find('.') == std::string::npos) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (language != "java" || name.find("types.") != 0) { | 
|  | // When generating java sources for "types.hal", output can be | 
|  | // constrained to just one of the top-level types declared | 
|  | // by using the extended syntax | 
|  | // android.hardware.Foo@1.0::types.TopLevelTypeName. | 
|  | // In all other cases (different language, not 'types') the dot | 
|  | // notation in the name is illegal in this context. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (language == "java") { | 
|  | bool isJavaCompatible; | 
|  | status_t err = isPackageJavaCompatible(fqName, coordinator, &isJavaCompatible); | 
|  | if (err != OK) return false; | 
|  |  | 
|  | if (!isJavaCompatible) { | 
|  | fprintf(stderr, | 
|  | "ERROR: %s is not Java compatible. The Java backend" | 
|  | " does NOT support union types nor native handles. " | 
|  | "In addition, vectors of arrays are limited to at most " | 
|  | "one-dimensional arrays and vectors of {vectors,interfaces} are" | 
|  | " not supported.\n", | 
|  | fqName.string().c_str()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | FileGenerator::GenerationFunction generateExportHeaderForPackage(bool forJava) { | 
|  | return [forJava](Formatter& out, const FQName& packageFQName, | 
|  | const Coordinator* coordinator) -> status_t { | 
|  | CHECK(!packageFQName.package().empty() && !packageFQName.version().empty() && | 
|  | packageFQName.name().empty()); | 
|  |  | 
|  | std::vector<FQName> packageInterfaces; | 
|  |  | 
|  | status_t err = coordinator->appendPackageInterfacesToVector( | 
|  | packageFQName, &packageInterfaces); | 
|  |  | 
|  | if (err != OK) { | 
|  | return err; | 
|  | } | 
|  |  | 
|  | std::vector<const Type *> exportedTypes; | 
|  |  | 
|  | for (const auto &fqName : packageInterfaces) { | 
|  | AST *ast = coordinator->parse(fqName); | 
|  |  | 
|  | if (ast == nullptr) { | 
|  | fprintf(stderr, | 
|  | "ERROR: Could not parse %s. Aborting.\n", | 
|  | fqName.string().c_str()); | 
|  |  | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | ast->appendToExportedTypesVector(&exportedTypes); | 
|  | } | 
|  |  | 
|  | if (exportedTypes.empty()) { | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | if (!out.isValid()) { | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | std::string packagePath; | 
|  | err = coordinator->getPackagePath(packageFQName, false /* relative */, | 
|  | false /* sanitized */, &packagePath); | 
|  | if (err != OK) return err; | 
|  |  | 
|  | out << "// This file is autogenerated by hidl-gen. Do not edit manually.\n" | 
|  | << "// Source: " << packageFQName.string() << "\n" | 
|  | << "// Location: " << packagePath << "\n\n"; | 
|  |  | 
|  | std::string guard; | 
|  | if (forJava) { | 
|  | out << "package " << packageFQName.javaPackage() << ";\n\n"; | 
|  | out << "public class Constants {\n"; | 
|  | out.indent(); | 
|  | } else { | 
|  | guard = "HIDL_GENERATED_"; | 
|  | guard += StringHelper::Uppercase(packageFQName.tokenName()); | 
|  | guard += "_"; | 
|  | guard += "EXPORTED_CONSTANTS_H_"; | 
|  |  | 
|  | out << "#ifndef " | 
|  | << guard | 
|  | << "\n#define " | 
|  | << guard | 
|  | << "\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"; | 
|  | } | 
|  |  | 
|  | for (const auto &type : exportedTypes) { | 
|  | type->emitExportedHeader(out, forJava); | 
|  | } | 
|  |  | 
|  | if (forJava) { | 
|  | out.unindent(); | 
|  | out << "}\n"; | 
|  | } else { | 
|  | out << "#ifdef __cplusplus\n}\n#endif\n\n#endif  // " | 
|  | << guard | 
|  | << "\n"; | 
|  | } | 
|  |  | 
|  | return OK; | 
|  | }; | 
|  | } | 
|  |  | 
|  | static status_t generateHashOutput(Formatter& out, const FQName& fqName, | 
|  | const Coordinator* coordinator) { | 
|  | CHECK(fqName.isFullyQualified()); | 
|  |  | 
|  | AST* ast = coordinator->parse(fqName, {} /* parsed */, | 
|  | Coordinator::Enforce::NO_HASH /* enforcement */); | 
|  |  | 
|  | if (ast == nullptr) { | 
|  | fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); | 
|  |  | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | out << Hash::getHash(ast->getFilename()).hexString() << " " << fqName.string() << "\n"; | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | static status_t generateFunctionCount(Formatter& out, const FQName& fqName, | 
|  | const Coordinator* coordinator) { | 
|  | CHECK(fqName.isFullyQualified()); | 
|  |  | 
|  | AST* ast = coordinator->parse(fqName, {} /* parsed */, | 
|  | Coordinator::Enforce::NO_HASH /* enforcement */); | 
|  |  | 
|  | if (ast == nullptr) { | 
|  | fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | const Interface* interface = ast->getInterface(); | 
|  | if (interface == nullptr) { | 
|  | fprintf(stderr, "ERROR: Function count requires interface: %s.\n", fqName.string().c_str()); | 
|  | return UNKNOWN_ERROR; | 
|  | } | 
|  |  | 
|  | // This is wrong for android.hidl.base@1.0::IBase, but in that case, it doesn't matter. | 
|  | // This is just the number of APIs that are added. | 
|  | out << fqName.string() << " " << interface->userDefinedMethods().size() << "\n"; | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<T> operator+(const std::vector<T>& lhs, const std::vector<T>& rhs) { | 
|  | std::vector<T> ret; | 
|  | ret.reserve(lhs.size() + rhs.size()); | 
|  | ret.insert(ret.begin(), lhs.begin(), lhs.end()); | 
|  | ret.insert(ret.end(), rhs.begin(), rhs.end()); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | // clang-format off | 
|  | static const std::vector<FileGenerator> kCppHeaderFormats = { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | [](const FQName& fqName) { return fqName.name() + ".h"; }, | 
|  | astGenerationFunction(&AST::generateInterfaceHeader), | 
|  | }, | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | [](const FQName& fqName) { | 
|  | return fqName.isInterfaceName() ? fqName.getInterfaceHwName() + ".h" : "hwtypes.h"; | 
|  | }, | 
|  | astGenerationFunction(&AST::generateHwBinderHeader), | 
|  | }, | 
|  | { | 
|  | FileGenerator::generateForInterfaces, | 
|  | [](const FQName& fqName) { return fqName.getInterfaceStubName() + ".h"; }, | 
|  | astGenerationFunction(&AST::generateStubHeader), | 
|  | }, | 
|  | { | 
|  | FileGenerator::generateForInterfaces, | 
|  | [](const FQName& fqName) { return fqName.getInterfaceProxyName() + ".h"; }, | 
|  | astGenerationFunction(&AST::generateProxyHeader), | 
|  | }, | 
|  | { | 
|  | FileGenerator::generateForInterfaces, | 
|  | [](const FQName& fqName) { return fqName.getInterfacePassthroughName() + ".h"; }, | 
|  | astGenerationFunction(&AST::generatePassthroughHeader), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const std::vector<FileGenerator> kCppSourceFormats = { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | [](const FQName& fqName) { | 
|  | return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + "All.cpp" : "types.cpp"; | 
|  | }, | 
|  | astGenerationFunction(&AST::generateCppSource), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const std::vector<FileGenerator> kCppImplHeaderFormats = { | 
|  | { | 
|  | FileGenerator::generateForInterfaces, | 
|  | [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".h"; }, | 
|  | astGenerationFunction(&AST::generateCppImplHeader), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const std::vector<FileGenerator> kCppImplSourceFormats = { | 
|  | { | 
|  | FileGenerator::generateForInterfaces, | 
|  | [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".cpp"; }, | 
|  | astGenerationFunction(&AST::generateCppImplSource), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const std::vector<FileGenerator> kCppAdapterHeaderFormats = { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | [](const FQName& fqName) { | 
|  | return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".h" : "Atypes.h"; | 
|  | }, | 
|  | astGenerationFunction(&AST::generateCppAdapterHeader), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const std::vector<FileGenerator> kCppAdapterSourceFormats = { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | [](const FQName& fqName) { | 
|  | return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".cpp" : "Atypes.cpp"; | 
|  | }, | 
|  | astGenerationFunction(&AST::generateCppAdapterSource), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static const std::vector<OutputHandler> kFormats = { | 
|  | { | 
|  | "check", | 
|  | "Parses the interface to see if valid but doesn't write any files.", | 
|  | OutputMode::NOT_NEEDED, | 
|  | Coordinator::Location::STANDARD_OUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | nullptr /* filename for fqname */, | 
|  | astGenerationFunction(), | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | "c++", | 
|  | "(internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_OUTPUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppHeaderFormats + kCppSourceFormats, | 
|  | }, | 
|  | { | 
|  | "c++-headers", | 
|  | "(internal) Generates C++ headers for interface files for talking to HIDL interfaces.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_OUTPUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppHeaderFormats, | 
|  | }, | 
|  | { | 
|  | "c++-sources", | 
|  | "(internal) Generates C++ sources for interface files for talking to HIDL interfaces.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_OUTPUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppSourceFormats, | 
|  | }, | 
|  | { | 
|  | "export-header", | 
|  | "Generates a header file from @export enumerations to help maintain legacy code.", | 
|  | OutputMode::NEEDS_FILE, | 
|  | Coordinator::Location::DIRECT, | 
|  | GenerationGranularity::PER_PACKAGE, | 
|  | validateIsPackage, | 
|  | {singleFileGenerator("", generateExportHeaderForPackage(false /* forJava */))} | 
|  | }, | 
|  | { | 
|  | "c++-impl", | 
|  | "Generates boilerplate implementation of a hidl interface in C++ (for convenience).", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::DIRECT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppImplHeaderFormats + kCppImplSourceFormats, | 
|  | }, | 
|  | { | 
|  | "c++-impl-headers", | 
|  | "c++-impl but headers only.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::DIRECT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppImplHeaderFormats, | 
|  | }, | 
|  | { | 
|  | "c++-impl-sources", | 
|  | "c++-impl but sources only.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::DIRECT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppImplSourceFormats, | 
|  | }, | 
|  | { | 
|  | "c++-adapter", | 
|  | "Takes a x.(y+n) interface and mocks an x.y interface.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_OUTPUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppAdapterHeaderFormats + kCppAdapterSourceFormats, | 
|  | }, | 
|  | { | 
|  | "c++-adapter-headers", | 
|  | "c++-adapter but helper headers only.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_OUTPUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppAdapterHeaderFormats, | 
|  | }, | 
|  | { | 
|  | "c++-adapter-sources", | 
|  | "c++-adapter but helper sources only.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_OUTPUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | kCppAdapterSourceFormats, | 
|  | }, | 
|  | { | 
|  | "c++-adapter-main", | 
|  | "c++-adapter but the adapter binary source only.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::DIRECT, | 
|  | GenerationGranularity::PER_PACKAGE, | 
|  | validateIsPackage, | 
|  | {singleFileGenerator("main.cpp", generateAdapterMainSource)}, | 
|  | }, | 
|  | { | 
|  | "java", | 
|  | "(internal) Generates Java library for talking to HIDL interfaces in Java.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_SANITIZED, | 
|  | GenerationGranularity::PER_TYPE, | 
|  | validateForSource, | 
|  | { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | [](const FQName& fqName) { | 
|  | return StringHelper::LTrim(fqName.name(), "types.") + ".java"; | 
|  | }, | 
|  | generateJavaForPackage, | 
|  | }, | 
|  | } | 
|  | }, | 
|  | { | 
|  | "java-constants", | 
|  | "(internal) Like export-header but for Java (always created by -Lmakefile if @export exists).", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_SANITIZED, | 
|  | GenerationGranularity::PER_PACKAGE, | 
|  | validateIsPackage, | 
|  | {singleFileGenerator("Constants.java", generateExportHeaderForPackage(true /* forJava */))} | 
|  | }, | 
|  | { | 
|  | "vts", | 
|  | "(internal) Generates vts proto files for use in vtsd.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::GEN_OUTPUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | [](const FQName& fqName) { | 
|  | return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + ".vts" : "types.vts"; | 
|  | }, | 
|  | astGenerationFunction(&AST::generateVts), | 
|  | }, | 
|  | } | 
|  | }, | 
|  | { | 
|  | "makefile", | 
|  | "(removed) Used to generate makefiles for -Ljava and -Ljava-constants.", | 
|  | OutputMode::NEEDS_SRC, | 
|  | Coordinator::Location::PACKAGE_ROOT, | 
|  | GenerationGranularity::PER_PACKAGE, | 
|  | [](const FQName &, const Coordinator*, const std::string &) { | 
|  | fprintf(stderr, "ERROR: makefile output is not supported. Use -Landroidbp for all build file generation.\n"); | 
|  | return false; | 
|  | }, | 
|  | {}, | 
|  | }, | 
|  | { | 
|  | "androidbp", | 
|  | "(internal) Generates Soong bp files for -Lc++-headers, -Lc++-sources, -Ljava, -Ljava-constants, and -Lc++-adapter.", | 
|  | OutputMode::NEEDS_SRC, | 
|  | Coordinator::Location::PACKAGE_ROOT, | 
|  | GenerationGranularity::PER_PACKAGE, | 
|  | validateIsPackage, | 
|  | {singleFileGenerator("Android.bp", generateAndroidBpForPackage)}, | 
|  | }, | 
|  | { | 
|  | "androidbp-impl", | 
|  | "Generates boilerplate bp files for implementation created with -Lc++-impl.", | 
|  | OutputMode::NEEDS_DIR, | 
|  | Coordinator::Location::DIRECT, | 
|  | GenerationGranularity::PER_PACKAGE, | 
|  | validateIsPackage, | 
|  | {singleFileGenerator("Android.bp", generateAndroidBpImplForPackage)}, | 
|  | }, | 
|  | { | 
|  | "hash", | 
|  | "Prints hashes of interface in `current.txt` format to standard out.", | 
|  | OutputMode::NOT_NEEDED, | 
|  | Coordinator::Location::STANDARD_OUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | nullptr /* file name for fqName */, | 
|  | generateHashOutput, | 
|  | }, | 
|  | } | 
|  | }, | 
|  | { | 
|  | "function-count", | 
|  | "Prints the total number of functions added by the package or interface.", | 
|  | OutputMode::NOT_NEEDED, | 
|  | Coordinator::Location::STANDARD_OUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | { | 
|  | { | 
|  | FileGenerator::generateForInterfaces, | 
|  | nullptr /* file name for fqName */, | 
|  | generateFunctionCount, | 
|  | }, | 
|  | } | 
|  | }, | 
|  | { | 
|  | "dependencies", | 
|  | "Prints all depended types.", | 
|  | OutputMode::NOT_NEEDED, | 
|  | Coordinator::Location::STANDARD_OUT, | 
|  | GenerationGranularity::PER_FILE, | 
|  | validateForSource, | 
|  | { | 
|  | { | 
|  | FileGenerator::alwaysGenerate, | 
|  | nullptr /* file name for fqName */, | 
|  | astGenerationFunction(&AST::generateDependencies), | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }; | 
|  | // clang-format on | 
|  |  | 
|  | static void usage(const char *me) { | 
|  | fprintf(stderr, | 
|  | "usage: %s [-p <root path>] -o <output path> -L <language> [-O <owner>] (-r <interface " | 
|  | "root>)+ [-R] [-v] [-d <depfile>] FQNAME...\n\n", | 
|  | me); | 
|  |  | 
|  | fprintf(stderr, | 
|  | "Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, to create output.\n\n"); | 
|  |  | 
|  | fprintf(stderr, "         -h: Prints this menu.\n"); | 
|  | fprintf(stderr, "         -L <language>: The following options are available:\n"); | 
|  | for (auto& e : kFormats) { | 
|  | fprintf(stderr, "            %-16s: %s\n", e.name().c_str(), e.description().c_str()); | 
|  | } | 
|  | fprintf(stderr, "         -O <owner>: The owner of the module for -Landroidbp(-impl)?.\n"); | 
|  | fprintf(stderr, "         -o <output path>: Location to output files.\n"); | 
|  | fprintf(stderr, "         -p <root path>: Android build root, defaults to $ANDROID_BUILD_TOP or pwd.\n"); | 
|  | fprintf(stderr, "         -R: Do not add default package roots if not specified in -r.\n"); | 
|  | fprintf(stderr, "         -r <package:path root>: E.g., android.hardware:hardware/interfaces.\n"); | 
|  | fprintf(stderr, "         -v: verbose output.\n"); | 
|  | fprintf(stderr, "         -d <depfile>: location of depfile to write to.\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); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | const OutputHandler* outputFormat = nullptr; | 
|  | Coordinator coordinator; | 
|  | std::string outputPath; | 
|  | bool suppressDefaultPackagePaths = false; | 
|  |  | 
|  | int res; | 
|  | while ((res = getopt(argc, argv, "hp:o:O:r:L:vd:R")) >= 0) { | 
|  | switch (res) { | 
|  | case 'p': { | 
|  | if (!coordinator.getRootPath().empty()) { | 
|  | fprintf(stderr, "ERROR: -p <root path> can only be specified once.\n"); | 
|  | exit(1); | 
|  | } | 
|  | coordinator.setRootPath(optarg); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case 'v': { | 
|  | coordinator.setVerbose(true); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case 'd': { | 
|  | coordinator.setDepFile(optarg); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case 'o': { | 
|  | if (!outputPath.empty()) { | 
|  | fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n"); | 
|  | exit(1); | 
|  | } | 
|  | outputPath = optarg; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case 'O': { | 
|  | if (!coordinator.getOwner().empty()) { | 
|  | fprintf(stderr, "ERROR: -O <owner> can only be specified once.\n"); | 
|  | exit(1); | 
|  | } | 
|  | coordinator.setOwner(optarg); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case 'r': { | 
|  | std::string val(optarg); | 
|  | auto index = val.find_first_of(':'); | 
|  | if (index == std::string::npos) { | 
|  | fprintf(stderr, "ERROR: -r option must contain ':': %s\n", val.c_str()); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | auto root = val.substr(0, index); | 
|  | auto path = val.substr(index + 1); | 
|  |  | 
|  | std::string error; | 
|  | status_t err = coordinator.addPackagePath(root, path, &error); | 
|  | if (err != OK) { | 
|  | fprintf(stderr, "%s\n", error.c_str()); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | break; | 
|  | } | 
|  |  | 
|  | case 'R': { | 
|  | suppressDefaultPackagePaths = true; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case 'L': { | 
|  | if (outputFormat != nullptr) { | 
|  | fprintf(stderr, | 
|  | "ERROR: only one -L option allowed. \"%s\" already specified.\n", | 
|  | outputFormat->name().c_str()); | 
|  | exit(1); | 
|  | } | 
|  | for (auto& e : kFormats) { | 
|  | if (e.name() == optarg) { | 
|  | outputFormat = &e; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (outputFormat == nullptr) { | 
|  | fprintf(stderr, | 
|  | "ERROR: unrecognized -L option: \"%s\".\n", | 
|  | optarg); | 
|  | exit(1); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case '?': | 
|  | case 'h': | 
|  | default: { | 
|  | usage(me); | 
|  | exit(1); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (coordinator.getRootPath().empty()) { | 
|  | const char* ANDROID_BUILD_TOP = getenv("ANDROID_BUILD_TOP"); | 
|  | if (ANDROID_BUILD_TOP != nullptr) { | 
|  | coordinator.setRootPath(ANDROID_BUILD_TOP); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (outputFormat == nullptr) { | 
|  | fprintf(stderr, | 
|  | "ERROR: no -L option provided.\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  | if (argc == 0) { | 
|  | fprintf(stderr, "ERROR: no fqname specified.\n"); | 
|  | usage(me); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | // Valid options are now in argv[0] .. argv[argc - 1]. | 
|  |  | 
|  | switch (outputFormat->mOutputMode) { | 
|  | case OutputMode::NEEDS_DIR: | 
|  | case OutputMode::NEEDS_FILE: { | 
|  | if (outputPath.empty()) { | 
|  | usage(me); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if (outputFormat->mOutputMode == OutputMode::NEEDS_DIR) { | 
|  | if (outputPath.back() != '/') { | 
|  | outputPath += "/"; | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | case OutputMode::NEEDS_SRC: { | 
|  | if (outputPath.empty()) { | 
|  | outputPath = coordinator.getRootPath(); | 
|  | } | 
|  | if (outputPath.back() != '/') { | 
|  | outputPath += "/"; | 
|  | } | 
|  |  | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | outputPath.clear();  // Unused. | 
|  | break; | 
|  | } | 
|  |  | 
|  | coordinator.setOutputPath(outputPath); | 
|  |  | 
|  | if (!suppressDefaultPackagePaths) { | 
|  | coordinator.addDefaultPackagePath("android.hardware", "hardware/interfaces"); | 
|  | coordinator.addDefaultPackagePath("android.hidl", "system/libhidl/transport"); | 
|  | coordinator.addDefaultPackagePath("android.frameworks", "frameworks/hardware/interfaces"); | 
|  | coordinator.addDefaultPackagePath("android.system", "system/hardware/interfaces"); | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < argc; ++i) { | 
|  | const char* arg = argv[i]; | 
|  |  | 
|  | FQName fqName; | 
|  | if (!FQName::parse(arg, &fqName)) { | 
|  | fprintf(stderr, "ERROR: Invalid fully-qualified name as argument: %s.\n", arg); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if (coordinator.getPackageInterfaceFiles(fqName, nullptr /*fileNames*/) != OK) { | 
|  | fprintf(stderr, "ERROR: Could not get sources for %s.\n", arg); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | // Dump extra verbose output | 
|  | if (coordinator.isVerbose()) { | 
|  | status_t err = | 
|  | dumpDefinedButUnreferencedTypeNames(fqName.getPackageAndVersion(), &coordinator); | 
|  | if (err != OK) return err; | 
|  | } | 
|  |  | 
|  | if (!outputFormat->validate(fqName, &coordinator, outputFormat->name())) { | 
|  | fprintf(stderr, | 
|  | "ERROR: output handler failed.\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | status_t err = outputFormat->generate(fqName, &coordinator); | 
|  | if (err != OK) exit(1); | 
|  |  | 
|  | err = outputFormat->writeDepFile(fqName, &coordinator); | 
|  | if (err != OK) exit(1); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |