Yet another backend for hidl-gen, this one generates a C-compatible header file

containing those enum types annotated in the package like so:

@export
enum Foo {
    ...
};

Optionally, the name to be used for the type declaration in the header file
can be different from that used in the .hal interface description by specifying

@export(name="foo_t")
enum Foo {
    ...
};

Finally, overriding the name to be empty, i.e.

@export(name="")
enum Foo {
    ...
};

will cause the generator to emit an anonymous enum.

Bug: 31800672
Change-Id: Idffb2c1700af1c7fd312941d80c3373add8ae558
Test: make
diff --git a/main.cpp b/main.cpp
index 91fc81c..b81f004 100644
--- a/main.cpp
+++ b/main.cpp
@@ -31,7 +31,12 @@
 
 struct OutputHandler {
     std::string mKey;
-    bool mNeedsOutputDir;
+    enum OutputMode {
+        NEEDS_DIR,
+        NEEDS_FILE,
+        NOT_NEEDED
+    } mOutputMode;
+
     enum ValRes {
         FAILED,
         PASS_PACKAGE,
@@ -678,9 +683,104 @@
     return OutputHandler::PASS_PACKAGE;
 }
 
+OutputHandler::ValRes validateForExportHeader(
+        const FQName &fqName, const std::string & /* language */) {
+    if (fqName.package().empty()) {
+        fprintf(stderr, "ERROR: Expecting package name\n");
+        return OutputHandler::FAILED;
+    }
+
+    if (fqName.version().empty()) {
+        fprintf(stderr, "ERROR: Expecting package version\n");
+        return OutputHandler::FAILED;
+    }
+
+    if (!fqName.name().empty()) {
+        fprintf(stderr,
+                "ERROR: Expecting only package name and version.\n");
+        return OutputHandler::FAILED;
+    }
+
+    return OutputHandler::PASS_PACKAGE;
+}
+
+
+static status_t generateExportHeaderForPackage(
+        const FQName &packageFQName,
+        const char * /* hidl_gen */,
+        Coordinator *coordinator,
+        const std::string &outputPath) {
+
+    CHECK(packageFQName.isValid()
+            && !packageFQName.isFullyQualified()
+            && 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 == NULL) {
+            fprintf(stderr,
+                    "ERROR: Could not parse %s. Aborting.\n",
+                    fqName.string().c_str());
+
+            return UNKNOWN_ERROR;
+        }
+
+        ast->appendToExportedTypesVector(&exportedTypes);
+    }
+
+    if (exportedTypes.empty()) {
+        return OK;
+    }
+
+    CHECK(Coordinator::MakeParentHierarchy(outputPath));
+    FILE *file = fopen(outputPath.c_str(), "w");
+
+    if (file == nullptr) {
+        return -errno;
+    }
+
+    Formatter out(file);
+
+    out << "// This file is autogenerated by hidl-gen. Do not edit manually."
+           "\n\n";
+
+    std::string guard = "HIDL_GENERATED_";
+    guard += 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);
+    }
+
+    out << "#ifdef __cplusplus\n}\n#endif\n\n#endif  // "
+        << guard
+        << "\n";
+
+    return OK;
+}
+
 static std::vector<OutputHandler> formats = {
     {"c++",
-     true /* mNeedsOutputDir */,
+     OutputHandler::NEEDS_DIR /* mOutputMode */,
      validateForSource,
      [](const FQName &fqName,
         const char *hidl_gen, Coordinator *coordinator,
@@ -701,8 +801,25 @@
         }
     },
 
+    {"export-header",
+     OutputHandler::NEEDS_FILE /* mOutputMode */,
+     validateForExportHeader,
+     [](const FQName &fqName,
+        const char *hidl_gen,
+        Coordinator *coordinator,
+        const std::string &outputPath) -> status_t {
+            CHECK(!fqName.isFullyQualified());
+
+            return generateExportHeaderForPackage(
+                    fqName,
+                    hidl_gen,
+                    coordinator,
+                    outputPath);
+        }
+    },
+
     {"c++-impl",
-     true /* mNeedsOutputDir */,
+     OutputHandler::NEEDS_DIR /* mOutputMode */,
      validateForSource,
      [](const FQName &fqName,
         const char *hidl_gen, Coordinator *coordinator,
@@ -723,7 +840,7 @@
 
 
     {"java",
-     true /* mNeedsOutputDir */,
+     OutputHandler::NEEDS_DIR /* mOutputMode */,
      validateForSource,
      [](const FQName &fqName,
         const char *hidl_gen, Coordinator *coordinator,
@@ -746,7 +863,7 @@
     },
 
     {"vts",
-     true,
+     OutputHandler::NEEDS_DIR /* mOutputMode */,
      validateForSource,
      [](const FQName &fqName,
         const char * hidl_gen,
@@ -767,19 +884,19 @@
     },
 
     {"makefile",
-     false /* mNeedsOutputDir */,
+     OutputHandler::NOT_NEEDED /* mOutputMode */,
      validateForMakefile,
      generateMakefileForPackage,
     },
 
     {"androidbp",
-     false /* mNeedsOutputDir */,
+     OutputHandler::NOT_NEEDED /* mOutputMode */,
      validateForMakefile,
      generateAndroidBpForPackage,
     },
 
     {"makefile-impl",
-     true /* mNeedsOutputDir */,
+     OutputHandler::NOT_NEEDED /* mOutputMode */,
      validateForMakefile,
      generateMakefileImplForPackage,
     }
@@ -804,7 +921,7 @@
 }
 
 int main(int argc, char **argv) {
-    std::string outputDir;
+    std::string outputPath;
     std::vector<std::string> packageRootPaths;
     std::vector<std::string> packageRoots;
 
@@ -816,7 +933,7 @@
         switch (res) {
             case 'o':
             {
-                outputDir = optarg;
+                outputPath = optarg;
                 break;
             }
 
@@ -881,16 +998,27 @@
 
     // Valid options are now in argv[0] .. argv[argc - 1].
 
-    if (!outputFormat->mNeedsOutputDir) {
-        outputDir.clear();  // Unused.
-    } else if (outputDir.empty()) {
-        usage(me);
-        exit(1);
-    } else {
-        const size_t len = outputDir.size();
-        if (outputDir[len - 1] != '/') {
-            outputDir += "/";
+    switch (outputFormat->mOutputMode) {
+        case OutputHandler::NEEDS_DIR:
+        case OutputHandler::NEEDS_FILE:
+        {
+            if (outputPath.empty()) {
+                usage(me);
+                exit(1);
+            }
+
+            if (outputFormat->mOutputMode == OutputHandler::NEEDS_DIR) {
+                const size_t len = outputPath.size();
+                if (outputPath[len - 1] != '/') {
+                    outputPath += "/";
+                }
+            }
+            break;
         }
+
+        default:
+            outputPath.clear();  // Unused.
+            break;
     }
 
     Coordinator coordinator(packageRootPaths, packageRoots);
@@ -912,7 +1040,7 @@
         }
 
         status_t err =
-            outputFormat->generate(fqName, me, &coordinator, outputDir);
+            outputFormat->generate(fqName, me, &coordinator, outputPath);
 
         if (err != OK) {
             exit(1);