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"};