/* * 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 "Coordinator.h" #include #include #include #include #include #include #include #include #include #include "AST.h" #include "Interface.h" #include "hidl-gen_l.h" static bool existdir(const char *name) { DIR *dir = opendir(name); if (dir == nullptr) { return false; } closedir(dir); return true; } namespace android { const std::string &Coordinator::getRootPath() const { return mRootPath; } void Coordinator::setRootPath(const std::string &rootPath) { mRootPath = rootPath; if (!mRootPath.empty() && !StringHelper::EndsWith(mRootPath, "/")) { mRootPath += "/"; } } void Coordinator::setOutputPath(const std::string& outputPath) { mOutputPath = outputPath; } void Coordinator::setVerbose(bool verbose) { mVerbose = verbose; } void Coordinator::setRequireFrozen(bool requireFrozen) { mRequireFrozen = requireFrozen; } bool Coordinator::isVerbose() const { return mVerbose; } void Coordinator::setDepFile(const std::string& depFile) { mDepFile = depFile; } const std::string& Coordinator::getOwner() const { return mOwner; } void Coordinator::setOwner(const std::string& owner) { mOwner = owner; } status_t Coordinator::addPackagePath(const std::string& root, const std::string& path, std::string* error) { FQName package = FQName(root, "0.0", ""); for (const PackageRoot &packageRoot : mPackageRoots) { if (packageRoot.root.inPackage(root) || package.inPackage(packageRoot.root.package())) { if (error != nullptr) { *error = "ERROR: conflicting package roots " + packageRoot.root.package() + " and " + root; } return UNKNOWN_ERROR; } } mPackageRoots.push_back({path, package}); return OK; } void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) { addPackagePath(root, path, nullptr /* error */); } Formatter Coordinator::getFormatter(const FQName& fqName, Location location, const std::string& fileName) const { if (location == Location::STANDARD_OUT) { return Formatter(stdout); } std::string filepath; status_t err = getFilepath(fqName, location, fileName, &filepath); if (err != OK) { return Formatter::invalid(); } onFileAccess(filepath, "w"); if (!Coordinator::MakeParentHierarchy(filepath)) { fprintf(stderr, "ERROR: could not make directories for %s.\n", filepath.c_str()); return Formatter::invalid(); } FILE* file = fopen(filepath.c_str(), "w"); if (file == nullptr) { fprintf(stderr, "ERROR: could not open file %s: %d\n", filepath.c_str(), errno); return Formatter::invalid(); } return Formatter(file); } status_t Coordinator::getFilepath(const FQName& fqName, Location location, const std::string& fileName, std::string* path) const { status_t err; std::string packagePath; std::string packageRootPath; switch (location) { case Location::DIRECT: { /* nothing */ *path = mOutputPath + fileName; } break; case Location::PACKAGE_ROOT: { err = getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath); if (err != OK) return err; *path = mOutputPath + packagePath + fileName; } break; case Location::GEN_OUTPUT: { err = convertPackageRootToPath(fqName, &packageRootPath); if (err != OK) return err; err = getPackagePath(fqName, true /* relative */, false /* sanitized */, &packagePath); if (err != OK) return err; *path = mOutputPath + packageRootPath + packagePath + fileName; } break; case Location::GEN_SANITIZED: { err = convertPackageRootToPath(fqName, &packageRootPath); if (err != OK) return err; err = getPackagePath(fqName, true /* relative */, true /* sanitized */, &packagePath); if (err != OK) return err; *path = mOutputPath + packageRootPath + packagePath + fileName; } break; default: { CHECK(false) << "Invalid location: " << static_cast(location); } } return OK; } void Coordinator::onFileAccess(const std::string& path, const std::string& mode) const { if (mode == "r") { // This is a global list. It's not cleared when a second fqname is processed for // two reasons: // 1). If there is a bug in hidl-gen, the dependencies on the first project from // the second would be required to recover correctly when the bug is fixed. // 2). This option is never used in Android builds. mReadFiles.insert(makeRelative(path)); } if (!mVerbose) { return; } fprintf(stderr, "VERBOSE: file access %s %s\n", path.c_str(), mode.c_str()); } status_t Coordinator::writeDepFile(const std::string& forFile) const { // No dep file requested if (mDepFile.empty()) return OK; onFileAccess(mDepFile, "w"); FILE* file = fopen(mDepFile.c_str(), "w"); if (file == nullptr) { fprintf(stderr, "ERROR: could not open dep file at %s.\n", mDepFile.c_str()); return UNKNOWN_ERROR; } Formatter out(file, 2 /* spacesPerIndent */); out << StringHelper::LTrim(forFile, mOutputPath) << ": \\\n"; out.indent([&] { for (const std::string& file : mReadFiles) { out << makeRelative(file) << " \\\n"; } }); return OK; } AST* Coordinator::parse(const FQName& fqName, std::set* parsedASTs, Enforce enforcement) const { AST* ret; status_t err = parseOptional(fqName, &ret, parsedASTs, enforcement); if (err != OK) CHECK(ret == nullptr); // internal consistency // only in a handful of places do we want to distinguish between // a missing file and a bad AST. Everywhere else, we just want to // throw an error if we expect an AST to be present but it is not. return ret; } status_t Coordinator::parseOptional(const FQName& fqName, AST** ast, std::set* parsedASTs, Enforce enforcement) const { CHECK(fqName.isFullyQualified()); auto it = mCache.find(fqName); if (it != mCache.end()) { *ast = (*it).second; if (*ast != nullptr && parsedASTs != nullptr) { parsedASTs->insert(*ast); } if (*ast == nullptr) { // circular import OR that AST has errors in it return UNKNOWN_ERROR; } return OK; } // Add this to the cache immediately, so we can discover circular imports. mCache[fqName] = nullptr; std::string packagePath; status_t err = getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath); if (err != OK) return err; const std::string path = makeAbsolute(packagePath + fqName.name() + ".hal"); *ast = new AST(this, &Hash::getHash(path)); if (fqName.name() != "types") { // If types.hal for this AST's package existed, make it's defined // types available to the (about to be parsed) AST right away. (*ast)->addImplicitImport(fqName.getTypesForPackage()); } std::unique_ptr> file(fopen(path.c_str(), "rb"), fclose); if (file == nullptr) { mCache.erase(fqName); // nullptr in cache is used to find circular imports delete *ast; *ast = nullptr; return OK; // File does not exist, nullptr AST* == file doesn't exist. } onFileAccess(path, "r"); // parse file takes ownership of file if (parseFile(*ast, std::move(file)) != OK || (*ast)->postParse() != OK) { delete *ast; *ast = nullptr; return UNKNOWN_ERROR; } if ((*ast)->package().package() != fqName.package() || (*ast)->package().version() != fqName.version()) { fprintf(stderr, "ERROR: File at '%s' does not match expected package and/or " "version.\n", path.c_str()); err = UNKNOWN_ERROR; } else { if ((*ast)->isInterface()) { if (fqName.name() == "types") { fprintf(stderr, "ERROR: File at '%s' declares an interface '%s' " "instead of the expected types common to the package.\n", path.c_str(), (*ast)->getInterface()->definedName().c_str()); err = UNKNOWN_ERROR; } else if ((*ast)->getInterface()->definedName() != fqName.name()) { fprintf(stderr, "ERROR: File at '%s' does not declare interface type " "'%s'.\n", path.c_str(), fqName.name().c_str()); err = UNKNOWN_ERROR; } } else if (fqName.name() != "types") { fprintf(stderr, "ERROR: File at '%s' declares types rather than the " "expected interface type '%s'.\n", path.c_str(), fqName.name().c_str()); err = UNKNOWN_ERROR; } else if ((*ast)->definesInterfaces()) { fprintf(stderr, "ERROR: types.hal file at '%s' declares at least one " "interface type.\n", path.c_str()); err = UNKNOWN_ERROR; } } if (err != OK) { delete *ast; *ast = nullptr; return err; } if (parsedASTs != nullptr) { parsedASTs->insert(*ast); } // put it into the cache now, so that enforceRestrictionsOnPackage can // parse fqName. mCache[fqName] = *ast; // For each .hal file that hidl-gen parses, the whole package will be checked. err = enforceRestrictionsOnPackage(fqName, enforcement); if (err != OK) { mCache[fqName] = nullptr; delete *ast; *ast = nullptr; return err; } return OK; } const Coordinator::PackageRoot* Coordinator::findPackageRoot(const FQName& fqName) const { CHECK(!fqName.package().empty()) << fqName.string(); // Find the right package prefix and path for this FQName. For // example, if FQName is "android.hardware.nfc@1.0::INfc", and the // prefix:root is set to [ "android.hardware:hardware/interfaces", // "vendor.qcom.hardware:vendor/qcom"], then we will identify the // prefix "android.hardware" and the package root // "hardware/interfaces". auto ret = mPackageRoots.end(); for (auto it = mPackageRoots.begin(); it != mPackageRoots.end(); it++) { if (!fqName.inPackage(it->root.package())) { continue; } if (ret != mPackageRoots.end()) { std::cerr << "ERROR: Multiple package roots found for " << fqName.string() << " (" << it->root.package() << " and " << ret->root.package() << ")\n"; return nullptr; } ret = it; } if (ret == mPackageRoots.end()) { std::cerr << "ERROR: Package root not specified for " << fqName.string() << "\n"; return nullptr; } return &(*ret); } std::string Coordinator::makeAbsolute(const std::string& path) const { if (StringHelper::StartsWith(path, "/") || mRootPath.empty()) { return path; } return mRootPath + path; } std::string Coordinator::makeRelative(const std::string& filename) const { return StringHelper::LTrim(filename, mRootPath); } status_t Coordinator::getPackageRoot(const FQName& fqName, std::string* root) const { const PackageRoot* packageRoot = findPackageRoot(fqName); if (packageRoot == nullptr) { return UNKNOWN_ERROR; } *root = packageRoot->root.package(); return OK; } status_t Coordinator::getPackageRootPath(const FQName& fqName, std::string* path) const { const PackageRoot* packageRoot = findPackageRoot(fqName); if (packageRoot == nullptr) { return UNKNOWN_ERROR; } *path = packageRoot->path; return OK; } status_t Coordinator::getPackagePath(const FQName& fqName, bool relative, bool sanitized, std::string* path) const { const PackageRoot* packageRoot = findPackageRoot(fqName); if (packageRoot == nullptr) return UNKNOWN_ERROR; // Given FQName of "android.hardware.nfc.test@1.0::IFoo" and a prefix // "android.hardware", the suffix is "nfc.test". std::string suffix = StringHelper::LTrim(fqName.package(), packageRoot->root.package()); suffix = StringHelper::LTrim(suffix, "."); std::vector suffixComponents; StringHelper::SplitString(suffix, '.', &suffixComponents); std::vector components; if (!relative) { components.push_back(StringHelper::RTrimAll(packageRoot->path, "/")); } components.insert(components.end(), suffixComponents.begin(), suffixComponents.end()); components.push_back(sanitized ? fqName.sanitizedVersion() : fqName.version()); *path = StringHelper::JoinStrings(components, "/") + "/"; return OK; } status_t Coordinator::getPackageInterfaceFiles( const FQName &package, std::vector *fileNames) const { if (fileNames) fileNames->clear(); std::string packagePath; status_t err = getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath); if (err != OK) return err; const std::string path = makeAbsolute(packagePath); std::unique_ptr dir(opendir(path.c_str()), closedir); if (dir == nullptr) { fprintf(stderr, "ERROR: Could not open package path %s for package %s:\n%s\n", packagePath.c_str(), package.string().c_str(), path.c_str()); return -errno; } if (fileNames == nullptr) { return OK; } struct dirent *ent; while ((ent = readdir(dir.get())) != nullptr) { // filesystems may not support d_type and return DT_UNKNOWN if (ent->d_type == DT_UNKNOWN) { struct stat sb; const auto filename = packagePath + std::string(ent->d_name); if (stat(filename.c_str(), &sb) == -1) { fprintf(stderr, "ERROR: Could not stat %s\n", filename.c_str()); return -errno; } if ((sb.st_mode & S_IFMT) != S_IFREG) { continue; } } else if (ent->d_type != DT_REG && ent->d_type != DT_LNK) { continue; } const auto suffix = ".hal"; const auto suffix_len = std::strlen(suffix); const auto d_namelen = strlen(ent->d_name); if (d_namelen < suffix_len || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) { continue; } fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len)); } std::sort(fileNames->begin(), fileNames->end(), [](const std::string& lhs, const std::string& rhs) -> bool { if (lhs == "types") { return true; } if (rhs == "types") { return false; } return lhs < rhs; }); return OK; } status_t Coordinator::appendPackageInterfacesToVector( const FQName &package, std::vector *packageInterfaces) const { packageInterfaces->clear(); std::vector fileNames; status_t err = getPackageInterfaceFiles(package, &fileNames); if (err != OK) { return err; } for (const auto &fileName : fileNames) { FQName subFQName(package.package(), package.version(), fileName); packageInterfaces->push_back(subFQName); } return OK; } status_t Coordinator::convertPackageRootToPath(const FQName& fqName, std::string* path) const { std::string packageRoot; status_t err = getPackageRoot(fqName, &packageRoot); if (err != OK) return err; if (*(packageRoot.end()--) != '.') { packageRoot += '.'; } std::replace(packageRoot.begin(), packageRoot.end(), '.', '/'); *path = packageRoot; // now converted to a path return OK; } status_t Coordinator::isTypesOnlyPackage(const FQName& package, bool* result) const { std::vector packageInterfaces; status_t err = appendPackageInterfacesToVector(package, &packageInterfaces); if (err != OK) { *result = false; return err; } *result = packageInterfaces.size() == 1 && packageInterfaces[0].name() == "types"; return OK; } status_t Coordinator::addUnreferencedTypes(const std::vector& packageInterfaces, std::set* unreferencedDefinitions, std::set* unreferencedImports) const { CHECK(unreferencedDefinitions != nullptr); CHECK(unreferencedImports != nullptr); std::set packageDefinedTypes; std::set packageReferencedTypes; std::set packageImportedTypes; std::set typesDefinedTypes; // only types.hal types for (const auto& fqName : packageInterfaces) { AST* ast = parse(fqName, nullptr /*imported*/, Coordinator::Enforce::NONE); if (!ast) { std::cerr << "ERROR: Could not parse " << fqName.string() << ". Aborting." << std::endl; return UNKNOWN_ERROR; } ast->addDefinedTypes(&packageDefinedTypes); ast->addReferencedTypes(&packageReferencedTypes); ast->getAllImportedNamesGranular(&packageImportedTypes); if (fqName.name() == "types") { ast->addDefinedTypes(&typesDefinedTypes); } } #if 0 for (const auto &fqName : packageDefinedTypes) { std::cout << "VERBOSE: DEFINED " << fqName.string() << std::endl; } for (const auto &fqName : packageImportedTypes) { std::cout << "VERBOSE: IMPORTED " << fqName.string() << std::endl; } for (const auto &fqName : packageReferencedTypes) { std::cout << "VERBOSE: REFERENCED " << fqName.string() << std::endl; } for (const auto &fqName : typesDefinedTypes) { std::cout << "VERBOSE: DEFINED in types.hal " << fqName.string() << std::endl; } #endif for (const auto& fqName : packageReferencedTypes) { packageDefinedTypes.erase(fqName); packageImportedTypes.erase(fqName); } // A package implicitly imports its own types.hal, only track them in one set. for (const auto& fqName : typesDefinedTypes) { packageImportedTypes.erase(fqName); } // defined but not referenced unreferencedDefinitions->insert(packageDefinedTypes.begin(), packageDefinedTypes.end()); // imported but not referenced unreferencedImports->insert(packageImportedTypes.begin(), packageImportedTypes.end()); return OK; } status_t Coordinator::enforceRestrictionsOnPackage(const FQName& fqName, Enforce enforcement) const { CHECK(enforcement == Enforce::FULL || enforcement == Enforce::NO_HASH || enforcement == Enforce::NONE); // need fqName to be something like android.hardware.foo@1.0. // name and valueName is ignored. if (fqName.package().empty() || fqName.version().empty()) { std::cerr << "ERROR: Cannot enforce restrictions on package " << fqName.string() << ": package or version is missing." << std::endl; return BAD_VALUE; } if (enforcement == Enforce::NONE) { return OK; } FQName package = fqName.getPackageAndVersion(); // look up cache. if (mPackagesEnforced.find(package) != mPackagesEnforced.end()) { return OK; } // enforce all rules. status_t err; err = enforceMinorVersionUprevs(package, enforcement); if (err != OK) { return err; } if (enforcement != Enforce::NO_HASH) { err = enforceHashes(package); if (err != OK) { return err; } } // cache it so that it won't need to be enforced again. mPackagesEnforced.insert(package); return OK; } status_t Coordinator::packageExists(const FQName& package, bool* result) const { std::string packagePath; status_t err = getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath); if (err != OK) return err; if (existdir(makeAbsolute(packagePath).c_str())) { *result = true; return OK; } *result = false; return OK; } status_t Coordinator::enforceMinorVersionUprevs(const FQName& currentPackage, Enforce enforcement) const { if(!currentPackage.hasVersion()) { std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string() << ": missing version." << std::endl; return UNKNOWN_ERROR; } if (currentPackage.getPackageMinorVersion() == 0) { return OK; // ignore for @x.0 } bool hasPrevPackage = false; FQName prevPackage = currentPackage; while (prevPackage.getPackageMinorVersion() > 0) { prevPackage = prevPackage.downRev(); bool result; status_t err = packageExists(prevPackage, &result); if (err != OK) return err; if (result) { hasPrevPackage = true; break; } } if (!hasPrevPackage) { // no @x.z, where z < y, exist. return OK; } if (prevPackage != currentPackage.downRev()) { std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string() << ": Found package " << prevPackage.string() << " but missing " << currentPackage.downRev().string() << "; you cannot skip a minor version." << std::endl; return UNKNOWN_ERROR; } bool prevIsTypesOnly; status_t err = isTypesOnlyPackage(prevPackage, &prevIsTypesOnly); if (err != OK) return err; if (prevIsTypesOnly) { // A types only package can be extended in any way. return OK; } std::vector packageInterfaces; err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces); if (err != OK) { return err; } bool extendedInterface = false; for (const FQName ¤tFQName : packageInterfaces) { if (currentFQName.name() == "types") { continue; // ignore types.hal } const Interface *iface = nullptr; AST* currentAST = parse(currentFQName, nullptr /* parsedASTs */, enforcement); if (currentAST != nullptr) { iface = currentAST->getInterface(); } if (iface == nullptr) { if (currentAST == nullptr) { std::cerr << "WARNING: Skipping " << currentFQName.string() << " because it could not be found or parsed" << " or " << currentPackage.string() << " doesn't pass all requirements." << std::endl; } else { std::cerr << "WARNING: Skipping " << currentFQName.string() << " because the file might contain more than one interface." << std::endl; } continue; } if (iface->superType() == nullptr) { CHECK(iface->isIBase()); continue; } // Assume that currentFQName == android.hardware.foo@2.2::IFoo. FQName lastFQName(prevPackage.package(), prevPackage.version(), currentFQName.name()); AST *lastAST = parse(lastFQName); for (; lastFQName.getPackageMinorVersion() > 0 && (lastAST == nullptr || lastAST->getInterface() == nullptr) ; lastFQName = lastFQName.downRev(), lastAST = parse(lastFQName)) { // nothing } // Then lastFQName == android.hardware.foo@2.1::IFoo or // lastFQName == android.hardware.foo@2.0::IFoo if 2.1 doesn't exist. bool lastFQNameExists = lastAST != nullptr && lastAST->getInterface() != nullptr; if (!lastFQNameExists) { continue; } if (iface->superType()->fqName() != lastFQName) { std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string() << ": " << iface->fqName().string() << " extends " << iface->superType()->fqName().string() << ", which is not allowed. It must extend " << lastFQName.string() << std::endl; return UNKNOWN_ERROR; } // at least one interface must extend the previous version // @2.0::IFoo does not work. It must be @2.1::IFoo for at least one interface. if (lastFQName.getPackageAndVersion() == prevPackage.getPackageAndVersion()) { extendedInterface = true; } if (mVerbose) { std::cout << "VERBOSE: EnforceMinorVersionUprevs: " << currentFQName.string() << " passes." << std::endl; } } if (!extendedInterface) { // No interface extends the interface with the same name in @x.(y-1). std::cerr << "ERROR: " << currentPackage.string() << " doesn't pass minor version uprev requirement. " << "Requires at least one interface to extend an interface with the same name " << "from " << prevPackage.string() << "." << std::endl; return UNKNOWN_ERROR; } return OK; } Coordinator::HashStatus Coordinator::checkHash(const FQName& fqName) const { AST* ast = parse(fqName); if (ast == nullptr) return HashStatus::ERROR; std::string rootPath; status_t err = getPackageRootPath(fqName, &rootPath); if (err != OK) return HashStatus::ERROR; std::string hashPath = makeAbsolute(rootPath) + "/current.txt"; std::string error; bool fileExists; std::vector frozen = Hash::lookupHash(hashPath, fqName.string(), &error, &fileExists); if (fileExists) onFileAccess(hashPath, "r"); if (error.size() > 0) { std::cerr << "ERROR: " << error << std::endl; return HashStatus::ERROR; } // hash not defined, interface not frozen if (frozen.size() == 0) { if (isVerbose()) { std::cerr << "VERBOSE: clearing runtime hash for " << fqName.string() << " because it is not frozen and so its hash cannot be depended upon as an " "indication of stability." << std::endl; } // This ensures that it can be detected. Hash::clearHash(ast->getFilename()); if (mRequireFrozen) { std::cerr << "ERROR: Unfrozen " << fqName.string() << " cannot be referenced from context specifying -F (require_frozen)." << std::endl; return HashStatus::ERROR; } return HashStatus::UNFROZEN; } std::string currentHash = ast->getFileHash()->hexString(); if (std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) { std::cerr << "ERROR: " << fqName.string() << " has hash " << currentHash << " which does not match hash on record. This interface has " << "been frozen. Do not change it!" << std::endl; return HashStatus::CHANGED; } return HashStatus::FROZEN; } status_t Coordinator::getUnfrozenDependencies(const FQName& fqName, std::set* result) const { CHECK(result != nullptr); AST* ast = parse(fqName); if (ast == nullptr) return UNKNOWN_ERROR; std::set imported; ast->getImportedPackages(&imported); // consider current package as dependency for types.hal and also to make // sure that if anything is frozen in a package, everything is. imported.insert(fqName.getPackageAndVersion()); // no circular dependency is already guaranteed by parsing // indirect dependencies will be checked when the imported interface frozen checks are done for (const FQName& importedPackage : imported) { std::vector packageInterfaces; status_t err = appendPackageInterfacesToVector(importedPackage, &packageInterfaces); if (err != OK) { return err; } for (const FQName& importedName : packageInterfaces) { HashStatus status = checkHash(importedName); switch (status) { case HashStatus::CHANGED: case HashStatus::ERROR: return UNKNOWN_ERROR; case HashStatus::FROZEN: continue; case HashStatus::UNFROZEN: result->insert(importedName); continue; default: LOG(FATAL) << static_cast(status); } } } return OK; } status_t Coordinator::enforceHashes(const FQName& currentPackage) const { std::vector packageInterfaces; status_t err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces); if (err != OK) { return err; } for (const FQName& currentFQName : packageInterfaces) { HashStatus status = checkHash(currentFQName); switch (status) { case HashStatus::CHANGED: case HashStatus::ERROR: return UNKNOWN_ERROR; case HashStatus::FROZEN: { std::set unfrozenDependencies; err = getUnfrozenDependencies(currentFQName, &unfrozenDependencies); if (err != OK) return err; if (!unfrozenDependencies.empty()) { std::cerr << "ERROR: Frozen interface " << currentFQName.string() << " cannot depend or be adjacent to unfrozen thing(s):" << std::endl; for (const FQName& name : unfrozenDependencies) { std::cerr << " (unfrozen) " << name.string() << std::endl; } return UNKNOWN_ERROR; } } continue; case HashStatus::UNFROZEN: continue; default: LOG(FATAL) << static_cast(status); } } return err; } bool Coordinator::MakeParentHierarchy(const std::string &path) { static const mode_t kMode = 0755; size_t start = 1; // Ignore leading '/' size_t slashPos; while ((slashPos = path.find('/', start)) != std::string::npos) { std::string partial = path.substr(0, slashPos); struct stat st; if (stat(partial.c_str(), &st) < 0) { if (errno != ENOENT) { return false; } int res = mkdir(partial.c_str(), kMode); if (res < 0) { return false; } } else if (!S_ISDIR(st.st_mode)) { return false; } start = slashPos + 1; } return true; } void Coordinator::emitOptionsUsageString(Formatter& out) { out << "[-p ] (-r )+ [-R] [-F] [-v] [-d ]"; } void Coordinator::emitOptionsDetailString(Formatter& out) { out << "-p : Android build root, defaults to $ANDROID_BUILD_TOP or pwd.\n" << "-R: Do not add default package roots if not specified in -r.\n" << "-F: Require all referenced ASTs to be frozen.\n" << "-r : E.g., android.hardware:hardware/interfaces.\n" << "-v: verbose output.\n" << "-d : location of depfile to write to.\n"; } void Coordinator::parseOptions(int argc, char** argv, const std::string& options, const HandleArg& handleArg) { // reset global state for getopt optind = 1; bool suppressDefaultPackagePaths = false; int res; std::string optstr = options + "p:r:RFvd:"; while ((res = getopt(argc, argv, optstr.c_str())) >= 0) { switch (res) { case 'v': { setVerbose(true); break; } case 'd': { setDepFile(optarg); break; } case 'p': { if (!getRootPath().empty()) { fprintf(stderr, "ERROR: -p can only be specified once.\n"); exit(1); } setRootPath(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 = addPackagePath(root, path, &error); if (err != OK) { fprintf(stderr, "%s\n", error.c_str()); exit(1); } break; } case 'R': { suppressDefaultPackagePaths = true; break; } case 'F': { setRequireFrozen(true); break; } // something downstream should handle these cases default: { handleArg(res, optarg); } } // glibc sets optarg to NULL for options without an argument, but POSIX doesn't // require it musl libc does not. Reset it here so that the next call to // handleArg doesn't pass a stale value. optarg = nullptr; } if (getRootPath().empty()) { const char* ANDROID_BUILD_TOP = getenv("ANDROID_BUILD_TOP"); if (ANDROID_BUILD_TOP != nullptr) { setRootPath(ANDROID_BUILD_TOP); } } if (!suppressDefaultPackagePaths) { addDefaultPackagePath("android.hardware", "hardware/interfaces"); addDefaultPackagePath("android.hidl", "system/libhidl/transport"); addDefaultPackagePath("android.frameworks", "frameworks/hardware/interfaces"); addDefaultPackagePath("android.system", "system/hardware/interfaces"); } } } // namespace android