blob: e440af65e5d37e5e87c2401796b057813dea51e7 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
#include <hidl-util/Formatter.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include "AST.h"
#include "AidlHelper.h"
#include "Coordinator.h"
#include "DocComment.h"
using namespace android;
static void usage(const char* me) {
Formatter out(stderr);
out << "Usage: " << me << " [-o <output path>] ";
Coordinator::emitOptionsUsageString(out);
out << " FQNAME...\n\n";
out << "Converts FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)? to an aidl "
"equivalent.\n\n";
out.indent();
out.indent();
out << "-o <output path>: Location to output files.\n";
out << "-h: Prints this menu.\n";
Coordinator::emitOptionsDetailString(out);
out.unindent();
out.unindent();
}
static const FQName& getNewerFQName(const FQName& lhs, const FQName& rhs) {
CHECK(lhs.package() == rhs.package());
CHECK(lhs.name() == rhs.name());
if (lhs.getPackageMajorVersion() > rhs.getPackageMajorVersion()) return lhs;
if (lhs.getPackageMajorVersion() < rhs.getPackageMajorVersion()) return rhs;
if (lhs.getPackageMinorVersion() > rhs.getPackageMinorVersion()) return lhs;
return rhs;
}
// If similar FQName is not found, the same one is returned
static FQName getLatestMinorVersionFQNameFromList(const FQName& fqName,
const std::vector<FQName>& list) {
FQName currentCandidate = fqName;
bool found = false;
for (const FQName& current : list) {
if (current.package() == currentCandidate.package() &&
current.name() == currentCandidate.name() &&
current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) {
// Prioritize elements in the list over the provided fqName
currentCandidate = found ? getNewerFQName(current, currentCandidate) : current;
found = true;
}
}
return currentCandidate;
}
static FQName getLatestMinorVersionNamedTypeFromList(const FQName& fqName,
const std::vector<const NamedType*>& list) {
FQName currentCandidate = fqName;
bool found = false;
for (const NamedType* currentNamedType : list) {
const FQName& current = currentNamedType->fqName();
if (current.package() == currentCandidate.package() &&
current.name() == currentCandidate.name() &&
current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) {
// Prioritize elements in the list over the provided fqName
currentCandidate = found ? getNewerFQName(current, currentCandidate) : current;
found = true;
}
}
return currentCandidate;
}
static bool packageExists(const Coordinator& coordinator, const FQName& fqName) {
bool result;
status_t err = coordinator.packageExists(fqName, &result);
if (err != OK) {
std::cerr << "Error trying to find package " << fqName.string() << std::endl;
exit(1);
}
return result;
}
static AST* parse(const Coordinator& coordinator, const FQName& target) {
AST* ast = coordinator.parse(target);
if (ast == nullptr) {
std::cerr << "ERROR: Could not parse " << target.name() << ". Aborting." << std::endl;
exit(1);
}
if (!ast->getUnhandledComments().empty()) {
AidlHelper::notes()
<< "Unhandled comments from " << target.string()
<< " follow. Consider using hidl-lint to locate these and fixup as many "
<< "as possible.\n";
for (const DocComment* docComment : ast->getUnhandledComments()) {
docComment->emit(AidlHelper::notes());
}
AidlHelper::notes() << "\n";
}
return ast;
}
// hidl is intentionally leaky. Turn off LeakSanitizer by default.
extern "C" const char* __asan_default_options() {
return "detect_leaks=0";
}
int main(int argc, char** argv) {
const char* me = argv[0];
if (argc == 1) {
usage(me);
std::cerr << "ERROR: no fqname specified." << std::endl;
exit(1);
}
Coordinator coordinator;
std::string outputPath;
coordinator.parseOptions(argc, argv, "ho:", [&](int res, char* arg) {
switch (res) {
case 'o': {
if (!outputPath.empty()) {
fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
exit(1);
}
outputPath = arg;
break;
}
case 'h':
case '?':
default: {
usage(me);
exit(1);
break;
}
}
});
if (!outputPath.empty() && outputPath.back() != '/') {
outputPath += "/";
}
coordinator.setOutputPath(outputPath);
argc -= optind;
argv += optind;
if (argc == 0) {
usage(me);
std::cerr << "ERROR: no fqname specified." << std::endl;
exit(1);
}
for (int i = 0; i < argc; ++i) {
const char* arg = argv[i];
FQName fqName;
if (!FQName::parse(arg, &fqName)) {
std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "."
<< std::endl;
exit(1);
}
if (!packageExists(coordinator, fqName)) {
std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl;
exit(1);
}
FQName currentFqName(fqName);
while (currentFqName.getPackageMinorVersion() != 0) {
if (!packageExists(coordinator, currentFqName.downRev())) break;
currentFqName = currentFqName.downRev();
}
std::vector<FQName> targets;
while (packageExists(coordinator, currentFqName)) {
std::vector<FQName> newTargets;
status_t err = coordinator.appendPackageInterfacesToVector(currentFqName, &newTargets);
if (err != OK) break;
targets.insert(targets.end(), newTargets.begin(), newTargets.end());
currentFqName = currentFqName.upRev();
}
// targets should not contain duplicates since appendPackageInterfaces is only called once
// per version. now remove all the elements that are not the "newest"
const auto& newEnd =
std::remove_if(targets.begin(), targets.end(), [&](const FQName& fqName) -> bool {
if (fqName.name() == "types") return false;
return getLatestMinorVersionFQNameFromList(fqName, targets) != fqName;
});
targets.erase(newEnd, targets.end());
if (fqName.isFullyQualified()) {
// Ensure that this fqName exists in the list.
// If not then there is a more recent version
if (std::find(targets.begin(), targets.end(), fqName) == targets.end()) {
// Not found. Error.
std::cerr << "ERROR: A newer minor version of " << fqName.string()
<< " exists. Compile that instead." << std::endl;
exit(1);
} else {
targets.clear();
targets.push_back(fqName);
}
}
// Set up AIDL conversion log
std::string aidlPackage = AidlHelper::getAidlPackage(fqName);
std::string aidlName = AidlHelper::getAidlName(fqName);
Formatter err = coordinator.getFormatter(
fqName, Coordinator::Location::DIRECT,
base::Join(base::Split(aidlPackage, "."), "/") + "/" +
(aidlName.empty() ? "" : (aidlName + "-")) + "conversion.log");
AidlHelper::setNotes(&err);
std::vector<const NamedType*> namedTypesInPackage;
for (const FQName& target : targets) {
if (target.name() != "types") continue;
AST* ast = parse(coordinator, target);
CHECK(!ast->isInterface());
std::vector<const NamedType*> types = ast->getRootScope().getSortedDefinedTypes();
namedTypesInPackage.insert(namedTypesInPackage.end(), types.begin(), types.end());
}
const auto& endNamedTypes = std::remove_if(
namedTypesInPackage.begin(), namedTypesInPackage.end(),
[&](const NamedType* namedType) -> bool {
return getLatestMinorVersionNamedTypeFromList(
namedType->fqName(), namedTypesInPackage) != namedType->fqName();
});
namedTypesInPackage.erase(endNamedTypes, namedTypesInPackage.end());
for (const NamedType* namedType : namedTypesInPackage) {
AidlHelper::emitAidl(*namedType, coordinator);
}
for (const FQName& target : targets) {
if (target.name() == "types") continue;
AST* ast = parse(coordinator, target);
const Interface* iface = ast->getInterface();
CHECK(iface);
AidlHelper::emitAidl(*iface, coordinator);
}
}
return 0;
}