Compile from API dump
AIDL compiler can now compile from API dump for the case when it is
required to create the libraries for old versions of the interface.
Bug: 113550618
Test: runtests.sh (unittests added)
Change-Id: I7f9999ad0a71858c73a99944744e042548f16093
diff --git a/aidl.cpp b/aidl.cpp
index 5597f9a..033e7bd 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -535,7 +535,7 @@
// Find files to import and parse them
vector<string> imports;
- ImportResolver import_resolver{io_delegate, options.ImportPaths(), options.InputFiles()};
+ ImportResolver import_resolver{io_delegate, options.ImportDirs(), options.InputFiles()};
for (const auto& import : main_parser->GetImports()) {
if (types->HasImportType(*import)) {
// There are places in the Android tree where an import doesn't resolve,
@@ -568,6 +568,24 @@
return err;
}
+ for (const auto& imported_file : options.ImportFiles()) {
+ imports.emplace_back(imported_file);
+
+ std::unique_ptr<Parser> import_parser =
+ Parser::Parse(imported_file, io_delegate, types->typenames_);
+ if (import_parser == nullptr) {
+ AIDL_ERROR(imported_file) << "error while importing " << imported_file;
+ err = AidlError::BAD_IMPORT;
+ continue;
+ }
+ if (!types->AddDefinedTypes(import_parser->GetDefinedTypes(), imported_file)) {
+ return AidlError::BAD_TYPE;
+ }
+ }
+ if (err != AidlError::OK) {
+ return err;
+ }
+
//////////////////////////////////////////////////////////////////////////
// Validation phase
//////////////////////////////////////////////////////////////////////////
@@ -578,12 +596,6 @@
return AidlError::BAD_INPUT;
}
- if (!is_check_api && main_parser->IsApiDump()) {
- AIDL_ERROR(input_file_name) << "Input is not AIDL source code, but "
- << "an AIDL dump file";
- return AidlError::BAD_INPUT;
- }
-
// Resolve the unresolved type references found from the input file
if (!is_check_api && !main_parser->Resolve()) {
// Resolution is not need for check api because all typespecs are
@@ -621,7 +633,10 @@
// Ensure that foo.bar.IFoo is defined in <some_path>/foo/bar/IFoo.aidl
// Do this only when there is only one type defined in the input file.
// When there are multiple types in a file, we can't satisfy the convention.
- if (!is_check_api && num_defined_types == 1 &&
+ // Also, we are not enforcing that when the input is from API dump because
+ // there are multiple types defined in an API dump and thus we can't follow
+ // the path rule.
+ if (num_defined_types == 1 && !main_parser->IsApiDump() &&
!check_filename(input_file_name, *defined_type)) {
return AidlError::BAD_PACKAGE;
}
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 0393333..1099fa1 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -699,14 +699,6 @@
EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
}
-TEST_F(AidlTest, RejectsApiDumpFileForCompilation) {
- Options options = Options::From("aidl --lang=java -o out p/IFoo.aidl");
- io_delegate_.SetFileContents(options.InputFiles().front(),
- "package p {"
- "interface IFoo { int getFoo(); }}");
- EXPECT_NE(0, ::android::aidl::compile_aidl(options, io_delegate_));
-}
-
TEST_F(AidlTest, RejectsSourceFileForApiCheck) {
Options options = Options::From("aidl --checkapi p/IFoo.aidl p/IBar.aidl");
io_delegate_.SetFileContents("p/IFoo.aidl", "package p; interface IFoo{}");
@@ -1017,5 +1009,34 @@
EXPECT_FALSE(::android::aidl::check_api(options, io_delegate_));
}
+TEST_F(AidlTest, CompileApiDumpJava) {
+ Options options = Options::From("aidl --lang=java -o out -m otherdump.aidl mydump.aidl");
+ io_delegate_.SetFileContents("mydump.aidl",
+ "package p {interface IFoo { void foo(in q.IBar a);}}");
+ io_delegate_.SetFileContents("otherdump.aidl", "package q {interface IBar{ }}");
+
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+ EXPECT_TRUE(io_delegate_.GetWrittenContents("out/p/IFoo.java", nullptr));
+ EXPECT_FALSE(io_delegate_.GetWrittenContents("out/q/IBar.java", nullptr));
+}
+
+TEST_F(AidlTest, CompileApiDumpCpp) {
+ Options options =
+ Options::From("aidl --lang=cpp -o out -h out/include -m otherdump.aidl mydump.aidl");
+ io_delegate_.SetFileContents("mydump.aidl",
+ "package p {interface IFoo { void foo(in q.IBar a);}}");
+ io_delegate_.SetFileContents("otherdump.aidl", "package q {interface IBar{ }}");
+
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+ EXPECT_TRUE(io_delegate_.GetWrittenContents("out/include/p/IFoo.h", nullptr));
+ EXPECT_TRUE(io_delegate_.GetWrittenContents("out/include/p/BnFoo.h", nullptr));
+ EXPECT_TRUE(io_delegate_.GetWrittenContents("out/include/p/BpFoo.h", nullptr));
+ EXPECT_TRUE(io_delegate_.GetWrittenContents("out/p/IFoo.cpp", nullptr));
+ EXPECT_FALSE(io_delegate_.GetWrittenContents("out/include/q/IBar.h", nullptr));
+ EXPECT_FALSE(io_delegate_.GetWrittenContents("out/include/q/BnBar.h", nullptr));
+ EXPECT_FALSE(io_delegate_.GetWrittenContents("out/include/q/BpBar.h", nullptr));
+ EXPECT_FALSE(io_delegate_.GetWrittenContents("out/q/IBar.cpp", nullptr));
+}
+
} // namespace aidl
} // namespace android
diff --git a/options.cpp b/options.cpp
index 0fb9e7d..aa4d0b1 100644
--- a/options.cpp
+++ b/options.cpp
@@ -66,6 +66,8 @@
sstr << "OPTION:" << endl
<< " -I DIR, --include=DIR" << endl
<< " Use DIR as a search path for import statements." << endl
+ << " -m FILE, --import=FILE" << endl
+ << " Import FILE directly without searching in the search paths." << endl
<< " -p FILE, --preprocessed=FILE" << endl
<< " Include FILE which is created by --preprocess." << endl
<< " -d FILE, --dep=FILE" << endl
@@ -143,6 +145,7 @@
{"dumpapi", no_argument, 0, 'u'},
{"checkapi", no_argument, 0, 'A'},
{"include", required_argument, 0, 'I'},
+ {"import", required_argument, 0, 'm'},
{"preprocessed", required_argument, 0, 'p'},
{"dep", required_argument, 0, 'd'},
{"out", required_argument, 0, 'o'},
@@ -155,8 +158,8 @@
{"help", no_argument, 0, 'e'},
{0, 0, 0, 0},
};
- const int c =
- getopt_long(argc, const_cast<char* const*>(argv), "I:p:d:o:h:abtv:", long_options, nullptr);
+ const int c = getopt_long(argc, const_cast<char* const*>(argv),
+ "I:m:p:d:o:h:abtv:", long_options, nullptr);
if (c == -1) {
// no more options
break;
@@ -201,7 +204,10 @@
}
break;
case 'I':
- import_paths_.emplace_back(Trim(optarg));
+ import_dirs_.emplace_back(Trim(optarg));
+ break;
+ case 'm':
+ import_files_.emplace_back(Trim(optarg));
break;
case 'p':
preprocessed_files_.emplace_back(Trim(optarg));
diff --git a/options.h b/options.h
index ed86149..f73f8ad 100644
--- a/options.h
+++ b/options.h
@@ -76,7 +76,9 @@
Task GetTask() const { return task_; }
- const vector<string>& ImportPaths() const { return import_paths_; }
+ const vector<string>& ImportDirs() const { return import_dirs_; }
+
+ const vector<string>& ImportFiles() const { return import_files_; }
const vector<string>& PreprocessedFiles() const { return preprocessed_files_; }
@@ -130,7 +132,8 @@
bool structured_ = false;
Language language_ = Language::UNSPECIFIED;
Task task_ = Task::COMPILE;
- vector<string> import_paths_;
+ vector<string> import_dirs_;
+ vector<string> import_files_;
vector<string> preprocessed_files_;
string dependency_file_;
bool gen_traces_ = false;
diff --git a/options_unittest.cpp b/options_unittest.cpp
index 92a967e..48d319f 100644
--- a/options_unittest.cpp
+++ b/options_unittest.cpp
@@ -114,7 +114,7 @@
unique_ptr<Options> options = GetOptions(kPreprocessCommand);
EXPECT_EQ(Options::Task::PREPROCESS, options->GetTask());
EXPECT_EQ(false, options->FailOnParcelable());
- EXPECT_EQ(0u, options->ImportPaths().size());
+ EXPECT_EQ(0u, options->ImportDirs().size());
EXPECT_EQ(0u, options->PreprocessedFiles().size());
EXPECT_EQ(string{kPreprocessCommandOutputFile}, options->OutputFile());
EXPECT_EQ(false, options->AutoDepFile());
@@ -129,7 +129,7 @@
EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
EXPECT_EQ(Options::Language::JAVA, options->TargetLanguage());
EXPECT_EQ(true, options->FailOnParcelable());
- EXPECT_EQ(1u, options->ImportPaths().size());
+ EXPECT_EQ(1u, options->ImportDirs().size());
EXPECT_EQ(0u, options->PreprocessedFiles().size());
EXPECT_EQ(string{kCompileCommandInput}, options->InputFiles().front());
EXPECT_EQ(string{kCompileCommandJavaOutput}, options->OutputFile());
@@ -142,7 +142,7 @@
EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
EXPECT_EQ(Options::Language::JAVA, options->TargetLanguage());
EXPECT_EQ(true, options->FailOnParcelable());
- EXPECT_EQ(1u, options->ImportPaths().size());
+ EXPECT_EQ(1u, options->ImportDirs().size());
EXPECT_EQ(0u, options->PreprocessedFiles().size());
EXPECT_EQ(string{kCompileCommandInput}, options->InputFiles().front());
EXPECT_EQ(string{kCompileCommandJavaOutput}, options->OutputFile());
@@ -152,8 +152,8 @@
TEST(OptionsTests, ParsesCompileCpp) {
unique_ptr<Options> options = GetOptions(kCompileCppCommand, Options::Language::CPP);
- ASSERT_EQ(1u, options->ImportPaths().size());
- EXPECT_EQ(string{kCompileCommandIncludePath}.substr(2), options->ImportPaths()[0]);
+ ASSERT_EQ(1u, options->ImportDirs().size());
+ EXPECT_EQ(string{kCompileCommandIncludePath}.substr(2), options->ImportDirs()[0]);
EXPECT_EQ(string{kCompileDepFile}.substr(2), options->DependencyFile());
EXPECT_EQ(false, options->DependencyFileNinja());
EXPECT_EQ(kCompileCommandInput, options->InputFiles().front());
@@ -163,8 +163,8 @@
TEST(OptionsTests, ParsesCompileCppNinja) {
unique_ptr<Options> options = GetOptions(kCompileCppCommandNinja, Options::Language::CPP);
- ASSERT_EQ(1u, options->ImportPaths().size());
- EXPECT_EQ(string{kCompileCommandIncludePath}.substr(2), options->ImportPaths()[0]);
+ ASSERT_EQ(1u, options->ImportDirs().size());
+ EXPECT_EQ(string{kCompileCommandIncludePath}.substr(2), options->ImportDirs()[0]);
EXPECT_EQ(string{kCompileDepFile}.substr(2), options->DependencyFile());
EXPECT_EQ(true, options->DependencyFileNinja());
EXPECT_EQ(kCompileCommandInput, options->InputFiles().front());
@@ -187,7 +187,7 @@
EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
EXPECT_EQ(Options::Language::JAVA, options->TargetLanguage());
EXPECT_EQ(false, options->FailOnParcelable());
- EXPECT_EQ(1u, options->ImportPaths().size());
+ EXPECT_EQ(1u, options->ImportDirs().size());
EXPECT_EQ(0u, options->PreprocessedFiles().size());
const vector<string> expected_input{"directory/input1.aidl", "directory/input2.aidl",
"directory/input3.aidl"};
@@ -237,7 +237,7 @@
EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
EXPECT_EQ(Options::Language::CPP, options->TargetLanguage());
EXPECT_EQ(false, options->FailOnParcelable());
- EXPECT_EQ(1u, options->ImportPaths().size());
+ EXPECT_EQ(1u, options->ImportDirs().size());
EXPECT_EQ(0u, options->PreprocessedFiles().size());
const vector<string> expected_input{"directory/input1.aidl", "directory/input2.aidl",
"directory/input3.aidl"};