You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1599 lines
53 KiB
1599 lines
53 KiB
/*
|
|
* 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 <sstream>
|
|
#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 GetFormatter = std::function<Formatter(void)>;
|
|
using GenerationFunction =
|
|
std::function<status_t(const FQName& fqName, const Coordinator* coordinator,
|
|
const GetFormatter& getFormatter)>;
|
|
|
|
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;
|
|
}
|
|
|
|
return mGenerationFunction(fqName, coordinator, [&] {
|
|
return coordinator->getFormatter(fqName, location, getFileName(fqName));
|
|
});
|
|
}
|
|
|
|
// 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->definedName());
|
|
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;
|
|
break;
|
|
}
|
|
|
|
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](const FQName& fqName, const Coordinator* coordinator,
|
|
const FileGenerator::GetFormatter& getFormatter) -> 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
|
|
|
|
Formatter out = getFormatter();
|
|
if (!out.isValid()) {
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
(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(const FQName& fqName, const Coordinator* coordinator,
|
|
const FileGenerator::GetFormatter& getFormatter) {
|
|
AST* ast;
|
|
std::string limitToType;
|
|
FQName typeName;
|
|
|
|
// See appendPerTypeTargets.
|
|
// 'a.b.c@1.0::types.Foo' is used to compile 'Foo' for Java even though in
|
|
// the rest of the compiler, this type is simply called 'a.b.c@1.0::Foo'.
|
|
// However, here, we need to disambiguate an interface name and a type in
|
|
// types.hal in order to figure out what to parse, so this legacy behavior
|
|
// is kept.
|
|
if (fqName.name().find("types.") == 0) {
|
|
limitToType = fqName.name().substr(strlen("types."));
|
|
|
|
ast = coordinator->parse(fqName.getTypesForPackage());
|
|
|
|
const auto& names = fqName.names();
|
|
CHECK(names.size() == 2 && names[0] == "types") << fqName.string();
|
|
typeName = FQName(fqName.package(), fqName.version(), names[1]);
|
|
} else {
|
|
ast = coordinator->parse(fqName);
|
|
typeName = fqName;
|
|
}
|
|
if (ast == nullptr) {
|
|
fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
Type* type = ast->lookupType(typeName, &ast->getRootScope());
|
|
CHECK(type != nullptr) << typeName.string();
|
|
if (!type->isJavaCompatible()) {
|
|
return OK;
|
|
}
|
|
|
|
Formatter out = getFormatter();
|
|
if (!out.isValid()) {
|
|
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.
|
|
|
|
std::vector<NamedType*> subTypes = typesAST->getRootScope().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 libhidlbase.
|
|
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");
|
|
}
|
|
|
|
// Keep the list of libs which are used by VNDK core libs and should be part of
|
|
// VNDK libs
|
|
static const std::vector<std::string> vndkLibs = {
|
|
"android.hardware.audio.common@2.0",
|
|
"android.hardware.configstore@1.0",
|
|
"android.hardware.configstore@1.1",
|
|
"android.hardware.graphics.allocator@2.0",
|
|
"android.hardware.graphics.allocator@3.0",
|
|
"android.hardware.graphics.allocator@4.0",
|
|
"android.hardware.graphics.bufferqueue@1.0",
|
|
"android.hardware.graphics.bufferqueue@2.0",
|
|
"android.hardware.media.bufferpool@2.0",
|
|
"android.hardware.media.omx@1.0",
|
|
"android.hardware.media@1.0",
|
|
"android.hardware.memtrack@1.0",
|
|
"android.hardware.soundtrigger@2.0",
|
|
"android.hidl.token@1.0",
|
|
"android.system.suspend@1.0",
|
|
};
|
|
|
|
bool isVndkCoreLib(const FQName& fqName) {
|
|
return std::find(vndkLibs.begin(), vndkLibs.end(), fqName.string()) != vndkLibs.end();
|
|
}
|
|
|
|
status_t hasVariantFile(const FQName& fqName, const Coordinator* coordinator,
|
|
const std::string& fileName, bool* isVariant) {
|
|
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, fileName, &path);
|
|
if (err != OK) return err;
|
|
|
|
const bool exists = fileExists(path);
|
|
|
|
if (exists) {
|
|
coordinator->onFileAccess(path, "r");
|
|
}
|
|
|
|
*isVariant = exists;
|
|
return OK;
|
|
}
|
|
|
|
status_t isSystemExtPackage(const FQName& fqName, const Coordinator* coordinator,
|
|
bool* isSystemExt) {
|
|
return hasVariantFile(fqName, coordinator, ".hidl_for_system_ext", isSystemExt);
|
|
}
|
|
|
|
status_t isOdmPackage(const FQName& fqName, const Coordinator* coordinator, bool* isOdm) {
|
|
return hasVariantFile(fqName, coordinator, ".hidl_for_odm", isOdm);
|
|
}
|
|
|
|
static status_t generateAdapterMainSource(const FQName& packageFQName,
|
|
const Coordinator* coordinator,
|
|
const FileGenerator::GetFormatter& getFormatter) {
|
|
std::vector<FQName> packageInterfaces;
|
|
status_t err =
|
|
coordinator->appendPackageInterfacesToVector(packageFQName,
|
|
&packageInterfaces);
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
// b/146223994: parse all interfaces
|
|
// - causes files to get read (filling out dep file)
|
|
// - avoid creating successful output for broken files
|
|
for (const FQName& 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;
|
|
}
|
|
}
|
|
|
|
Formatter out = getFormatter();
|
|
if (!out.isValid()) {
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
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(const FQName& packageFQName,
|
|
const Coordinator* coordinator,
|
|
const FileGenerator::GetFormatter& getFormatter) {
|
|
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 isCoreAndroid = isCoreAndroidPackage(packageFQName);
|
|
|
|
bool isVndkCore = isVndkCoreLib(packageFQName);
|
|
bool isVndkSp = isSystemProcessSupportedPackage(packageFQName);
|
|
|
|
bool isSystemExtHidl;
|
|
err = isSystemExtPackage(packageFQName, coordinator, &isSystemExtHidl);
|
|
if (err != OK) return err;
|
|
bool isSystemExt = isSystemExtHidl || !isCoreAndroid;
|
|
|
|
bool isForOdm;
|
|
err = isOdmPackage(packageFQName, coordinator, &isForOdm);
|
|
if (err != OK) return err;
|
|
|
|
std::string packageRoot;
|
|
err = coordinator->getPackageRoot(packageFQName, &packageRoot);
|
|
if (err != OK) return err;
|
|
|
|
Formatter out = getFormatter();
|
|
if (!out.isValid()) {
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
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 (isVndkCore || isVndkSp) {
|
|
out << "vndk: ";
|
|
out.block([&]() {
|
|
out << "enabled: true,\n";
|
|
if (isVndkSp) {
|
|
out << "support_system_process: true,\n";
|
|
}
|
|
}) << ",\n";
|
|
}
|
|
if (isSystemExt) {
|
|
out << "system_ext_specific: true,\n";
|
|
}
|
|
if (isForOdm) {
|
|
out << "odm_available: 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();
|
|
|
|
return OK;
|
|
}
|
|
|
|
static status_t generateAndroidBpImplForPackage(const FQName& packageFQName,
|
|
const Coordinator* coordinator,
|
|
const FileGenerator::GetFormatter& getFormatter) {
|
|
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);
|
|
}
|
|
|
|
Formatter out = getFormatter();
|
|
if (!out.isValid()) {
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
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"
|
|
<< "\"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. In addition, vectors of arrays are limited to at most one-dimensional "
|
|
"arrays and vectors of {vectors,interfaces,memory} are not supported.\n",
|
|
fqName.string().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool validateForFormat(const FQName& fqName, const Coordinator* coordinator,
|
|
const std::string& format) {
|
|
CHECK_EQ(format, "format");
|
|
|
|
if (!validateForSource(fqName, coordinator, format)) return false;
|
|
|
|
std::vector<FQName> packageInterfaces;
|
|
|
|
if (fqName.isFullyQualified()) {
|
|
packageInterfaces.push_back(fqName);
|
|
} else {
|
|
status_t err = coordinator->appendPackageInterfacesToVector(fqName, &packageInterfaces);
|
|
if (err != OK) return err;
|
|
}
|
|
|
|
bool destroysInformation = false;
|
|
|
|
for (const auto& fqName : packageInterfaces) {
|
|
AST* ast = coordinator->parse(fqName);
|
|
|
|
if (ast->getUnhandledComments().size() > 0) {
|
|
destroysInformation = true;
|
|
for (const auto& i : ast->getUnhandledComments()) {
|
|
std::cerr << "Unrecognized comment at " << i->location() << std::endl;
|
|
Formatter err(stderr);
|
|
err.indent();
|
|
i->emit(err);
|
|
err.unindent();
|
|
err.endl();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (destroysInformation) {
|
|
std::cerr << "\nhidl-gen does not support comments at these locations, and formatting "
|
|
"the file would destroy them. HIDL doc comments need to be multiline comments "
|
|
"(/*...*/) before specific elements. This will also cause them to be emitted "
|
|
"in output files in the correct locations so that IDE users or people "
|
|
"inspecting generated source can see them in the correct location. Formatting "
|
|
"the file would destroy these comments.\n";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
FileGenerator::GenerationFunction generateExportHeaderForPackage(bool forJava) {
|
|
return [forJava](const FQName& packageFQName, const Coordinator* coordinator,
|
|
const FileGenerator::GetFormatter& getFormatter) -> 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()) {
|
|
fprintf(stderr,
|
|
"ERROR: -Ljava-constants (Android.bp: gen_java_constants) requested for %s, "
|
|
"but no types declare @export.",
|
|
packageFQName.string().c_str());
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
Formatter out = getFormatter();
|
|
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(const FQName& fqName, const Coordinator* coordinator,
|
|
const FileGenerator::GetFormatter& getFormatter) {
|
|
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;
|
|
}
|
|
|
|
Formatter out = getFormatter();
|
|
if (!out.isValid()) {
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
out << Hash::getHash(ast->getFilename()).hexString() << " " << fqName.string() << "\n";
|
|
|
|
return OK;
|
|
}
|
|
|
|
static status_t generateFunctionCount(const FQName& fqName, const Coordinator* coordinator,
|
|
const FileGenerator::GetFormatter& getFormatter) {
|
|
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;
|
|
}
|
|
|
|
Formatter out = getFormatter();
|
|
if (!out.isValid()) {
|
|
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-impl",
|
|
"Generates boilerplate implementation of a hidl interface in Java (for convenience).",
|
|
OutputMode::NEEDS_DIR,
|
|
Coordinator::Location::DIRECT,
|
|
GenerationGranularity::PER_FILE,
|
|
validateForSource,
|
|
{
|
|
{
|
|
FileGenerator::generateForInterfaces,
|
|
[](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".java"; },
|
|
astGenerationFunction(&AST::generateJavaImpl),
|
|
},
|
|
}
|
|
},
|
|
{
|
|
"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),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"inheritance-hierarchy",
|
|
"Prints the hierarchy of inherited types as a JSON object.",
|
|
OutputMode::NOT_NEEDED,
|
|
Coordinator::Location::STANDARD_OUT,
|
|
GenerationGranularity::PER_FILE,
|
|
validateForSource,
|
|
{
|
|
{
|
|
FileGenerator::alwaysGenerate,
|
|
nullptr /* file name for fqName */,
|
|
astGenerationFunction(&AST::generateInheritanceHierarchy),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"format",
|
|
"Reformats the .hal files",
|
|
OutputMode::NEEDS_SRC,
|
|
Coordinator::Location::PACKAGE_ROOT,
|
|
GenerationGranularity::PER_FILE,
|
|
validateForFormat,
|
|
{
|
|
{
|
|
FileGenerator::alwaysGenerate,
|
|
[](const FQName& fqName) { return fqName.name() + ".hal"; },
|
|
astGenerationFunction(&AST::generateFormattedHidl),
|
|
},
|
|
}
|
|
},
|
|
};
|
|
// clang-format on
|
|
|
|
static void usage(const char* me) {
|
|
Formatter out(stderr);
|
|
|
|
out << "Usage: " << me << " -o <output path> -L <language> [-O <owner>] ";
|
|
Coordinator::emitOptionsUsageString(out);
|
|
out << " FQNAME...\n\n";
|
|
|
|
out << "Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, to create output.\n\n";
|
|
|
|
out.indent();
|
|
out.indent();
|
|
|
|
out << "-h: Prints this menu.\n";
|
|
out << "-L <language>: The following options are available:\n";
|
|
out.indent([&] {
|
|
for (auto& e : kFormats) {
|
|
std::stringstream sstream;
|
|
sstream.fill(' ');
|
|
sstream.width(16);
|
|
sstream << std::left << e.name();
|
|
|
|
out << sstream.str() << ": " << e.description() << "\n";
|
|
}
|
|
});
|
|
out << "-O <owner>: The owner of the module for -Landroidbp(-impl)?.\n";
|
|
out << "-o <output path>: Location to output files.\n";
|
|
Coordinator::emitOptionsDetailString(out);
|
|
|
|
out.unindent();
|
|
out.unindent();
|
|
}
|
|
|
|
// 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;
|
|
|
|
coordinator.parseOptions(argc, argv, "ho:O:L:", [&](int res, char* arg) {
|
|
switch (res) {
|
|
case 'o': {
|
|
if (!outputPath.empty()) {
|
|
fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
|
|
exit(1);
|
|
}
|
|
outputPath = arg;
|
|
break;
|
|
}
|
|
|
|
case 'O': {
|
|
if (!coordinator.getOwner().empty()) {
|
|
fprintf(stderr, "ERROR: -O <owner> can only be specified once.\n");
|
|
exit(1);
|
|
}
|
|
coordinator.setOwner(arg);
|
|
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() == arg) {
|
|
outputFormat = &e;
|
|
break;
|
|
}
|
|
}
|
|
if (outputFormat == nullptr) {
|
|
fprintf(stderr, "ERROR: unrecognized -L option: \"%s\".\n", arg);
|
|
exit(1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case '?':
|
|
case 'h':
|
|
default: {
|
|
usage(me);
|
|
exit(1);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
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);
|
|
|
|
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: Validation 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;
|
|
}
|