Refactored assemble_vintf.cpp

for better readability.

Test: builds (assemble_vintf is run to
check framework/device compatibility matrix / manfiests.

Change-Id: I48d80137c1cf886326ccf83e0406bd80eac8452b
diff --git a/assemble_vintf.cpp b/assemble_vintf.cpp
index f6df2d3..fb96359 100644
--- a/assemble_vintf.cpp
+++ b/assemble_vintf.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <getopt.h>
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -55,89 +56,108 @@
         return ss.str();
     }
 
-    static bool assemble(std::basic_istream<char>& inFile,
-                         std::basic_ostream<char>& outFile,
-                         std::ifstream& checkFile,
-                         bool outputMatrix) {
+    std::basic_ostream<char>& out() const {
+        return mOutFileRef == nullptr ? std::cout : *mOutFileRef;
+    }
+
+    bool assembleHalManifest(HalManifest* halManifest) {
         std::string error;
-        std::string fileContent = read(inFile);
+
+        if (halManifest->mType == SchemaType::DEVICE) {
+            if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) {
+                return false;
+            }
+        }
+
+        if (mOutputMatrix) {
+            CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix();
+            if (!halManifest->checkCompatibility(generatedMatrix, &error)) {
+                std::cerr << "FATAL ERROR: cannot generate a compatible matrix: " << error
+                          << std::endl;
+            }
+            out() << "<!-- \n"
+                     "    Autogenerated skeleton compatibility matrix. \n"
+                     "    Use with caution. Modify it to suit your needs.\n"
+                     "    All HALs are set to optional.\n"
+                     "    Many entries other than HALs are zero-filled and\n"
+                     "    require human attention. \n"
+                     "-->\n"
+                  << gCompatibilityMatrixConverter(generatedMatrix);
+        } else {
+            out() << gHalManifestConverter(*halManifest);
+        }
+        out().flush();
+
+        if (mCheckFile.is_open()) {
+            CompatibilityMatrix checkMatrix;
+            if (!gCompatibilityMatrixConverter(&checkMatrix, read(mCheckFile))) {
+                std::cerr << "Cannot parse check file as a compatibility matrix: "
+                          << gCompatibilityMatrixConverter.lastError() << std::endl;
+                return false;
+            }
+            if (!halManifest->checkCompatibility(checkMatrix, &error)) {
+                std::cerr << "Not compatible: " << error << std::endl;
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    bool assembleCompatibilityMatrix(CompatibilityMatrix* matrix) {
+        std::string error;
+
+        KernelSepolicyVersion kernelSepolicyVers;
+        Version sepolicyVers;
+        if (matrix->mType == SchemaType::FRAMEWORK) {
+            if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) {
+                return false;
+            }
+            if (!getFlag("POLICYVERS", &kernelSepolicyVers)) {
+                return false;
+            }
+            matrix->framework.mSepolicy =
+                Sepolicy(kernelSepolicyVers, {{sepolicyVers.majorVer, sepolicyVers.minorVer}});
+        }
+        out() << gCompatibilityMatrixConverter(*matrix);
+        out().flush();
+
+        if (mCheckFile.is_open()) {
+            HalManifest checkManifest;
+            if (!gHalManifestConverter(&checkManifest, read(mCheckFile))) {
+                std::cerr << "Cannot parse check file as a HAL manifest: "
+                          << gHalManifestConverter.lastError() << std::endl;
+                return false;
+            }
+            if (!checkManifest.checkCompatibility(*matrix, &error)) {
+                std::cerr << "Not compatible: " << error << std::endl;
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    bool assemble() {
+        if (!mInFile.is_open()) {
+            std::cerr << "Missing input file." << std::endl;
+            return false;
+        }
+
+        std::string fileContent = read(mInFile);
 
         HalManifest halManifest;
         if (gHalManifestConverter(&halManifest, fileContent)) {
-            if (halManifest.mType == SchemaType::DEVICE) {
-                if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest.device.mSepolicyVersion)) {
-                    return false;
-                }
+            if (assembleHalManifest(&halManifest)) {
+                return true;
             }
-
-            if (outputMatrix) {
-                CompatibilityMatrix generatedMatrix = halManifest.generateCompatibleMatrix();
-                if (!halManifest.checkCompatibility(generatedMatrix, &error)) {
-                    std::cerr << "FATAL ERROR: cannot generate a compatible matrix: "
-                              << error << std::endl;
-                }
-                outFile << "<!-- \n"
-                           "    Autogenerated skeleton compatibility matrix. \n"
-                           "    Use with caution. Modify it to suit your needs.\n"
-                           "    All HALs are set to optional.\n"
-                           "    Many entries other than HALs are zero-filled and\n"
-                           "    require human attention. \n"
-                           "-->\n"
-                        << gCompatibilityMatrixConverter(generatedMatrix);
-            } else {
-                outFile << gHalManifestConverter(halManifest);
-            }
-            outFile.flush();
-
-            if (checkFile.is_open()) {
-                CompatibilityMatrix checkMatrix;
-                if (!gCompatibilityMatrixConverter(&checkMatrix, read(checkFile))) {
-                    std::cerr << "Cannot parse check file as a compatibility matrix: "
-                              << gCompatibilityMatrixConverter.lastError()
-                              << std::endl;
-                    return false;
-                }
-                if (!halManifest.checkCompatibility(checkMatrix, &error)) {
-                    std::cerr << "Not compatible: " << error << std::endl;
-                    return false;
-                }
-            }
-
-            return true;
         }
 
         CompatibilityMatrix matrix;
         if (gCompatibilityMatrixConverter(&matrix, fileContent)) {
-            KernelSepolicyVersion kernelSepolicyVers;
-            Version sepolicyVers;
-            if (matrix.mType == SchemaType::FRAMEWORK) {
-                if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) {
-                    return false;
-                }
-                if (!getFlag("POLICYVERS", &kernelSepolicyVers)) {
-                    return false;
-                }
-                matrix.framework.mSepolicy = Sepolicy(kernelSepolicyVers,
-                        {{sepolicyVers.majorVer,sepolicyVers.minorVer}});
+            if (assembleCompatibilityMatrix(&matrix)) {
+                return true;
             }
-            outFile << gCompatibilityMatrixConverter(matrix);
-            outFile.flush();
-
-            if (checkFile.is_open()) {
-                HalManifest checkManifest;
-                if (!gHalManifestConverter(&checkManifest, read(checkFile))) {
-                    std::cerr << "Cannot parse check file as a HAL manifest: "
-                              << gHalManifestConverter.lastError()
-                              << std::endl;
-                    return false;
-                }
-                if (!checkManifest.checkCompatibility(matrix, &error)) {
-                    std::cerr << "Not compatible: " << error << std::endl;
-                    return false;
-                }
-            }
-
-            return true;
         }
 
         std::cerr << "Input file has unknown format." << std::endl
@@ -147,73 +167,92 @@
                   << gCompatibilityMatrixConverter.lastError() << std::endl;
         return false;
     }
+
+    bool openOutFile(const char* path) {
+        mOutFileRef = std::make_unique<std::ofstream>();
+        mOutFileRef->open(path);
+        return mOutFileRef->is_open();
+    }
+
+    bool openInFile(const char* path) {
+        mInFile.open(path);
+        return mInFile.is_open();
+    }
+
+    bool openCheckFile(const char* path) {
+        mCheckFile.open(path);
+        return mCheckFile.is_open();
+    }
+
+    void setOutputMatrix() { mOutputMatrix = true; }
+
+   private:
+    std::ifstream mInFile;
+    std::unique_ptr<std::ofstream> mOutFileRef;
+    std::ifstream mCheckFile;
+    bool mOutputMatrix = false;
 };
 
 }  // namespace vintf
 }  // namespace android
 
 void help() {
-    std::cerr <<
-"assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
-"    fill in build-time flags into the given file.\n"
-"assemble_vintf -h\n"
-"               Display this help text.\n"
-"assemble_vintf -i <input file> [-o <output file>] [-m] [-c [<check file>]]\n"
-"               Fill in build-time flags into the given file.\n"
-"    -i <input file>\n"
-"               Input file. Format is automatically detected.\n"
-"    -o <input file>\n"
-"               Optional output file. If not specified, write to stdout.\n"
-"    -m\n"
-"               a compatible compatibility matrix is\n"
-"               generated instead; for example, given a device manifest,\n"
-"               a framework compatibility matrix is generated. This flag\n"
-"               is ignored when input is a compatibility matrix.\n"
-"    -c [<check file>]\n"
-"               After writing the output file, check compatibility between\n"
-"               output file and check file.\n"
-"               If -c is set but the check file is not specified, a warning\n"
-"               message is written to stderr. Return 0.\n"
-"               If the check file is specified but is not compatible, an error\n"
-"               message is written to stderr. Return 1.\n";
+    std::cerr << "assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
+                 "    fill in build-time flags into the given file.\n"
+                 "assemble_vintf -h\n"
+                 "               Display this help text.\n"
+                 "assemble_vintf -i <input file> [-o <output file>] [-m] [-c [<check file>]]\n"
+                 "               [--kernel=<version>:<android-base.cfg>]\n"
+                 "               Fill in build-time flags into the given file.\n"
+                 "    -i <input file>\n"
+                 "               Input file. Format is automatically detected.\n"
+                 "    -o <output file>\n"
+                 "               Optional output file. If not specified, write to stdout.\n"
+                 "    -m\n"
+                 "               a compatible compatibility matrix is\n"
+                 "               generated instead; for example, given a device manifest,\n"
+                 "               a framework compatibility matrix is generated. This flag\n"
+                 "               is ignored when input is a compatibility matrix.\n"
+                 "    -c [<check file>]\n"
+                 "               After writing the output file, check compatibility between\n"
+                 "               output file and check file.\n"
+                 "               If -c is set but the check file is not specified, a warning\n"
+                 "               message is written to stderr. Return 0.\n"
+                 "               If the check file is specified but is not compatible, an error\n"
+                 "               message is written to stderr. Return 1.\n";
 }
 
 int main(int argc, char **argv) {
-    std::ifstream inFile;
-    std::ofstream outFile;
-    std::ifstream checkFile;
-    std::ostream* outFileRef = &std::cout;
-    bool outputMatrix = false;
+    const struct option longopts[] = {{0, 0, 0, 0}};
+
     std::string inFilePath;
+    ::android::vintf::AssembleVintf assembleVintf;
     int res;
-    while((res = getopt(argc, argv, "hi:o:mc:")) >= 0) {
+    int optind;
+    while ((res = getopt_long(argc, argv, "hi:o:mc:", longopts, &optind)) >= 0) {
         switch (res) {
             case 'i': {
                 inFilePath = optarg;
-                inFile.open(optarg);
-                if (!inFile.is_open()) {
+                if (!assembleVintf.openInFile(optarg)) {
                     std::cerr << "Failed to open " << optarg << std::endl;
                     return 1;
                 }
             } break;
 
             case 'o': {
-                outFile.open(optarg);
-                if (!outFile.is_open()) {
+                if (!assembleVintf.openOutFile(optarg)) {
                     std::cerr << "Failed to open " << optarg << std::endl;
                     return 1;
                 }
-                outFileRef = &outFile;
             } break;
 
             case 'm': {
-                outputMatrix = true;
+                assembleVintf.setOutputMatrix();
             } break;
 
             case 'c': {
                 if (strlen(optarg) != 0) {
-                    checkFile.open(optarg);
-                    if (!checkFile.is_open()) {
+                    if (!assembleVintf.openCheckFile(optarg)) {
                         std::cerr << "Failed to open " << optarg << std::endl;
                         return 1;
                     }
@@ -231,15 +270,7 @@
         }
     }
 
-    if (!inFile.is_open()) {
-        std::cerr << "Missing input file" << std::endl;
-        help();
-        return 1;
-    }
-
-    bool success = ::android::vintf::AssembleVintf::assemble(
-            inFile, *outFileRef, checkFile, outputMatrix);
-
+    bool success = assembleVintf.assemble();
 
     return success ? 0 : 1;
 }