Auto-generate java libraries for compatible packages.

Change-Id: I753f8bc5ab8ca6b5293c75b548897bbdec16aa65
Bug: 31221188
diff --git a/main.cpp b/main.cpp
index 8e8d055..e7d959a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -18,6 +18,7 @@
 #include "Coordinator.h"
 #include "Formatter.h"
 #include "FQName.h"
+#include "Scope.h"
 
 #include <android-base/logging.h>
 #include <set>
@@ -36,7 +37,7 @@
         PASS_PACKAGE,
         PASS_FULL
     };
-    ValRes (*validate)(const FQName &);
+    ValRes (*validate)(const FQName &, const std::string &language);
     status_t (*generate)(const FQName &fqName,
                          const char *hidl_gen,
                          Coordinator *coordinator,
@@ -51,7 +52,19 @@
         const std::string &lang) {
     CHECK(fqName.isFullyQualified());
 
-    AST *ast = coordinator->parse(fqName);
+    AST *ast;
+    const char *limitToType = nullptr;
+
+    if (fqName.name().find("types.") == 0) {
+        CHECK(lang == "java");  // Already verified in validate().
+
+        limitToType = fqName.name().c_str() + strlen("types.");
+
+        FQName typesName(fqName.package(), fqName.version(), "types");
+        ast = coordinator->parse(typesName);
+    } else {
+        ast = coordinator->parse(fqName);
+    }
 
     if (ast == NULL) {
         fprintf(stderr,
@@ -65,7 +78,7 @@
         return ast->generateCpp(outputDir);
     }
     if (lang == "java") {
-        return ast->generateJava(outputDir);
+        return ast->generateJava(outputDir, limitToType);
     }
     if (lang == "vts") {
         return ast->generateVts(outputDir);
@@ -109,6 +122,110 @@
     return packageFQName.string();
 }
 
+static void generateMakefileSectionForLanguageAndType(
+        Formatter &out,
+        Coordinator *coordinator,
+        const FQName &packageFQName,
+        const FQName &fqName,
+        const char *typeName,
+        bool forJava) {
+    out << "\n"
+        << "\n#"
+        << "\n# Build " << fqName.name() << ".hal";
+
+    if (forJava && typeName != nullptr) {
+        out << " (" << typeName << ")";
+    }
+
+    out << "\n#"
+        << "\nGEN := $(intermediates)/"
+        << coordinator->convertPackageRootToPath(packageFQName)
+        << coordinator->getPackagePath(packageFQName, true /* relative */);
+    if (!forJava) {
+        CHECK(typeName == nullptr);
+
+        if (fqName.name() == "types") {
+            out << "types.cpp";
+        } else {
+            out << fqName.name().substr(1) << "All.cpp";
+        }
+    } else if (typeName == nullptr) {
+        out << fqName.name() << ".java";
+    } else {
+        out << typeName << ".java";
+    }
+
+    out << "\n$(GEN): $(HIDL)";
+    out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
+    out << "\n$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/"
+        << fqName.name() << ".hal";
+    out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
+        << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
+    out.indent();
+    out.indent();
+    out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
+        << "\n-L"
+        << (forJava ? "java" : "c++")
+        << " -r"
+        << coordinator->getPackageRoot(packageFQName) << ":"
+        << coordinator->getPackageRootPath(packageFQName) << " \\\n";
+
+    out << packageFQName.string()
+        << "::"
+        << fqName.name();
+
+    if (forJava && typeName != nullptr) {
+        out << "." << typeName;
+    }
+
+    out << "\n";
+
+    out.unindent();
+    out.unindent();
+
+    out << "\n$(GEN): $(LOCAL_PATH)/" << fqName.name() << ".hal";
+    out << "\n\t$(transform-generated-source)";
+    out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
+}
+
+static void generateMakefileSectionForLanguage(
+        Formatter &out,
+        Coordinator *coordinator,
+        const FQName &packageFQName,
+        const std::vector<FQName> &packageInterfaces,
+        AST *typesAST,
+        bool forJava) {
+    for (const auto &fqName : packageInterfaces) {
+        if (forJava && fqName.name() == "types") {
+            CHECK(typesAST != nullptr);
+
+            Scope *rootScope = typesAST->scope();
+            for (size_t i = 0; i < rootScope->countTypes(); ++i) {
+                std::string typeName;
+                rootScope->typeAt(i, &typeName);
+
+                generateMakefileSectionForLanguageAndType(
+                        out,
+                        coordinator,
+                        packageFQName,
+                        fqName,
+                        typeName.c_str(),
+                        forJava);
+            }
+
+            continue;
+        }
+
+        generateMakefileSectionForLanguageAndType(
+                out,
+                coordinator,
+                packageFQName,
+                fqName,
+                nullptr /* typeName */,
+                forJava);
+    }
+}
+
 static status_t generateMakefileForPackage(
         const FQName &packageFQName,
         const char *hidl_gen,
@@ -130,6 +247,9 @@
     }
 
     std::set<FQName> importedPackages;
+    bool packageIsJavaCompatible = true;
+    AST *typesAST = nullptr;
+
     for (const auto &fqName : packageInterfaces) {
         AST *ast = coordinator->parse(fqName);
 
@@ -141,6 +261,14 @@
             return UNKNOWN_ERROR;
         }
 
+        if (packageIsJavaCompatible && !ast->isJavaCompatible()) {
+            packageIsJavaCompatible = false;
+        }
+
+        if (fqName.name() == "types") {
+            typesAST = ast;
+        }
+
         ast->getImportedPackages(&importedPackages);
     }
 
@@ -170,44 +298,13 @@
         << "HIDL := $(HOST_OUT_EXECUTABLES)/"
         << hidl_gen << "$(HOST_EXECUTABLE_SUFFIX)";
 
-    for (const auto &fqName : packageInterfaces) {
-
-        out << "\n"
-            << "\n#"
-            << "\n# Build " << fqName.name() << ".hal"
-            << "\n#";
-        out << "\nGEN := $(intermediates)/"
-            << coordinator->convertPackageRootToPath(packageFQName)
-            << coordinator->getPackagePath(packageFQName, true /* relative */);
-        if (fqName.name() == "types") {
-            out << "types.cpp";
-        } else {
-            out << fqName.name().substr(1) << "All.cpp";
-        }
-
-        out << "\n$(GEN): $(HIDL)";
-        out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
-        out << "\n$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/"
-            << fqName.name() << ".hal";
-        out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
-            << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
-        out.indent();
-        out.indent();
-        out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
-            << "\n-Lc++ -r"
-            << coordinator->getPackageRoot(packageFQName) << ":"
-            << coordinator->getPackageRootPath(packageFQName) << "\\";
-        out << "\n"
-            << packageFQName.string()
-            << "::$(patsubst %.hal,%,$(notdir $(PRIVATE_DEPS)))"
-            << "\n";
-        out.unindent();
-        out.unindent();
-
-        out << "\n$(GEN): $(LOCAL_PATH)/" << fqName.name() << ".hal";
-        out << "\n\t$(transform-generated-source)";
-        out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
-    }
+    generateMakefileSectionForLanguage(
+            out,
+            coordinator,
+            packageFQName,
+            packageInterfaces,
+            typesAST,
+            false /* forJava */);
 
     out << "\n"
         << "\nLOCAL_EXPORT_C_INCLUDE_DIRS := $(intermediates)"
@@ -226,10 +323,37 @@
 
     out << "\ninclude $(BUILD_SHARED_LIBRARY)\n";
 
+    if (packageIsJavaCompatible) {
+        out << "\n"
+            << "########################################"
+            << "########################################\n\n";
+
+        out << "include $(CLEAR_VARS)\n"
+            << "LOCAL_MODULE := "
+            << libraryName
+            << "-java\n"
+            << "LOCAL_MODULE_CLASS := JAVA_LIBRARIES\n\n"
+            << "intermediates := $(local-generated-sources-dir)\n\n"
+            << "HIDL := $(HOST_OUT_EXECUTABLES)/"
+            << hidl_gen
+            << "$(HOST_EXECUTABLE_SUFFIX)";
+
+        generateMakefileSectionForLanguage(
+                out,
+                coordinator,
+                packageFQName,
+                packageInterfaces,
+                typesAST,
+                true /* forJava */);
+
+        out << "\ninclude $(BUILD_JAVA_LIBRARY)\n";
+    }
+
     return OK;
 }
 
-OutputHandler::ValRes validateForMakefile(const FQName &fqName) {
+OutputHandler::ValRes validateForMakefile(
+        const FQName &fqName, const std::string & /* language */) {
     if (fqName.package().empty()) {
         fprintf(stderr, "ERROR: Expecting package name\n");
         return OutputHandler::FAILED;
@@ -249,7 +373,8 @@
     return OutputHandler::PASS_PACKAGE;
 }
 
-OutputHandler::ValRes validateForSource(const FQName &fqName) {
+OutputHandler::ValRes validateForSource(
+        const FQName &fqName, const std::string &language) {
     if (fqName.package().empty()) {
         fprintf(stderr, "ERROR: Expecting package name\n");
         return OutputHandler::FAILED;
@@ -260,9 +385,26 @@
         return OutputHandler::FAILED;
     }
 
-    return fqName.name().empty() ?
-        OutputHandler::PASS_PACKAGE :
-        OutputHandler::PASS_FULL;
+    const std::string &name = fqName.name();
+    if (!name.empty()) {
+        if (name.find('.') == std::string::npos) {
+            return OutputHandler::PASS_FULL;
+        }
+
+        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 OutputHandler::FAILED;
+        }
+
+        return OutputHandler::PASS_FULL;
+    }
+
+    return OutputHandler::PASS_PACKAGE;
 }
 
 static std::vector<OutputHandler> formats = {
@@ -276,12 +418,14 @@
                         return generateSourcesForFile(fqName,
                                                       hidl_gen,
                                                       coordinator,
-                                                      outputDir, "c++");
+                                                      outputDir,
+                                                      "c++");
             } else {
                         return generateSourcesForPackage(fqName,
                                                          hidl_gen,
                                                          coordinator,
-                                                         outputDir, "c++");
+                                                         outputDir,
+                                                         "c++");
             }
         }
     },
@@ -296,13 +440,15 @@
                 return generateSourcesForFile(fqName,
                                               hidl_gen,
                                               coordinator,
-                                              outputDir, "java");
+                                              outputDir,
+                                              "java");
             }
             else {
                 return generateSourcesForPackage(fqName,
                                                  hidl_gen,
                                                  coordinator,
-                                                 outputDir, "java");
+                                                 outputDir,
+                                                 "java");
             }
         }
     },
@@ -454,7 +600,9 @@
             exit(1);
         }
 
-        OutputHandler::ValRes valid = outputFormat->validate(fqName);
+        OutputHandler::ValRes valid =
+            outputFormat->validate(fqName, outputFormat->mKey);
+
         if (valid == OutputHandler::FAILED) {
             exit(1);
         }