Home | History | Annotate | Download | only in libvintf
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <getopt.h>
     18 #include <stdlib.h>
     19 #include <unistd.h>
     20 
     21 #include <fstream>
     22 #include <iostream>
     23 #include <unordered_map>
     24 #include <sstream>
     25 #include <string>
     26 
     27 #include <vintf/parse_string.h>
     28 #include <vintf/parse_xml.h>
     29 
     30 namespace android {
     31 namespace vintf {
     32 
     33 /**
     34  * Slurps the device manifest file and add build time flag to it.
     35  */
     36 class AssembleVintf {
     37 public:
     38     template<typename T>
     39     static bool getFlag(const std::string& key, T* value) {
     40         const char *envValue = getenv(key.c_str());
     41         if (envValue == NULL) {
     42             std::cerr << "Required " << key << " flag." << std::endl;
     43             return false;
     44         }
     45 
     46         if (!parse(envValue, value)) {
     47             std::cerr << "Cannot parse " << envValue << "." << std::endl;
     48             return false;
     49         }
     50         return true;
     51     }
     52 
     53     static std::string read(std::basic_istream<char>& is) {
     54         std::stringstream ss;
     55         ss << is.rdbuf();
     56         return ss.str();
     57     }
     58 
     59     std::basic_ostream<char>& out() const {
     60         return mOutFileRef == nullptr ? std::cout : *mOutFileRef;
     61     }
     62 
     63     bool assembleHalManifest(HalManifest* halManifest) {
     64         std::string error;
     65 
     66         if (halManifest->mType == SchemaType::DEVICE) {
     67             if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) {
     68                 return false;
     69             }
     70         }
     71 
     72         if (mOutputMatrix) {
     73             CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix();
     74             if (!halManifest->checkCompatibility(generatedMatrix, &error)) {
     75                 std::cerr << "FATAL ERROR: cannot generate a compatible matrix: " << error
     76                           << std::endl;
     77             }
     78             out() << "<!-- \n"
     79                      "    Autogenerated skeleton compatibility matrix. \n"
     80                      "    Use with caution. Modify it to suit your needs.\n"
     81                      "    All HALs are set to optional.\n"
     82                      "    Many entries other than HALs are zero-filled and\n"
     83                      "    require human attention. \n"
     84                      "-->\n"
     85                   << gCompatibilityMatrixConverter(generatedMatrix);
     86         } else {
     87             out() << gHalManifestConverter(*halManifest);
     88         }
     89         out().flush();
     90 
     91         if (mCheckFile.is_open()) {
     92             CompatibilityMatrix checkMatrix;
     93             if (!gCompatibilityMatrixConverter(&checkMatrix, read(mCheckFile))) {
     94                 std::cerr << "Cannot parse check file as a compatibility matrix: "
     95                           << gCompatibilityMatrixConverter.lastError() << std::endl;
     96                 return false;
     97             }
     98             if (!halManifest->checkCompatibility(checkMatrix, &error)) {
     99                 std::cerr << "Not compatible: " << error << std::endl;
    100                 return false;
    101             }
    102         }
    103 
    104         return true;
    105     }
    106 
    107     bool assembleCompatibilityMatrix(CompatibilityMatrix* matrix) {
    108         std::string error;
    109 
    110         KernelSepolicyVersion kernelSepolicyVers;
    111         Version sepolicyVers;
    112         if (matrix->mType == SchemaType::FRAMEWORK) {
    113             if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) {
    114                 return false;
    115             }
    116             if (!getFlag("POLICYVERS", &kernelSepolicyVers)) {
    117                 return false;
    118             }
    119             matrix->framework.mSepolicy =
    120                 Sepolicy(kernelSepolicyVers, {{sepolicyVers.majorVer, sepolicyVers.minorVer}});
    121         }
    122         out() << gCompatibilityMatrixConverter(*matrix);
    123         out().flush();
    124 
    125         if (mCheckFile.is_open()) {
    126             HalManifest checkManifest;
    127             if (!gHalManifestConverter(&checkManifest, read(mCheckFile))) {
    128                 std::cerr << "Cannot parse check file as a HAL manifest: "
    129                           << gHalManifestConverter.lastError() << std::endl;
    130                 return false;
    131             }
    132             if (!checkManifest.checkCompatibility(*matrix, &error)) {
    133                 std::cerr << "Not compatible: " << error << std::endl;
    134                 return false;
    135             }
    136         }
    137 
    138         return true;
    139     }
    140 
    141     bool assemble() {
    142         if (!mInFile.is_open()) {
    143             std::cerr << "Missing input file." << std::endl;
    144             return false;
    145         }
    146 
    147         std::string fileContent = read(mInFile);
    148 
    149         HalManifest halManifest;
    150         if (gHalManifestConverter(&halManifest, fileContent)) {
    151             if (assembleHalManifest(&halManifest)) {
    152                 return true;
    153             }
    154         }
    155 
    156         CompatibilityMatrix matrix;
    157         if (gCompatibilityMatrixConverter(&matrix, fileContent)) {
    158             if (assembleCompatibilityMatrix(&matrix)) {
    159                 return true;
    160             }
    161         }
    162 
    163         std::cerr << "Input file has unknown format." << std::endl
    164                   << "Error when attempting to convert to manifest: "
    165                   << gHalManifestConverter.lastError() << std::endl
    166                   << "Error when attempting to convert to compatibility matrix: "
    167                   << gCompatibilityMatrixConverter.lastError() << std::endl;
    168         return false;
    169     }
    170 
    171     bool openOutFile(const char* path) {
    172         mOutFileRef = std::make_unique<std::ofstream>();
    173         mOutFileRef->open(path);
    174         return mOutFileRef->is_open();
    175     }
    176 
    177     bool openInFile(const char* path) {
    178         mInFile.open(path);
    179         return mInFile.is_open();
    180     }
    181 
    182     bool openCheckFile(const char* path) {
    183         mCheckFile.open(path);
    184         return mCheckFile.is_open();
    185     }
    186 
    187     void setOutputMatrix() { mOutputMatrix = true; }
    188 
    189    private:
    190     std::ifstream mInFile;
    191     std::unique_ptr<std::ofstream> mOutFileRef;
    192     std::ifstream mCheckFile;
    193     bool mOutputMatrix = false;
    194 };
    195 
    196 }  // namespace vintf
    197 }  // namespace android
    198 
    199 void help() {
    200     std::cerr << "assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
    201                  "    fill in build-time flags into the given file.\n"
    202                  "assemble_vintf -h\n"
    203                  "               Display this help text.\n"
    204                  "assemble_vintf -i <input file> [-o <output file>] [-m] [-c [<check file>]]\n"
    205                  "               [--kernel=<version>:<android-base.cfg>]\n"
    206                  "               Fill in build-time flags into the given file.\n"
    207                  "    -i <input file>\n"
    208                  "               Input file. Format is automatically detected.\n"
    209                  "    -o <output file>\n"
    210                  "               Optional output file. If not specified, write to stdout.\n"
    211                  "    -m\n"
    212                  "               a compatible compatibility matrix is\n"
    213                  "               generated instead; for example, given a device manifest,\n"
    214                  "               a framework compatibility matrix is generated. This flag\n"
    215                  "               is ignored when input is a compatibility matrix.\n"
    216                  "    -c [<check file>]\n"
    217                  "               After writing the output file, check compatibility between\n"
    218                  "               output file and check file.\n"
    219                  "               If -c is set but the check file is not specified, a warning\n"
    220                  "               message is written to stderr. Return 0.\n"
    221                  "               If the check file is specified but is not compatible, an error\n"
    222                  "               message is written to stderr. Return 1.\n";
    223 }
    224 
    225 int main(int argc, char **argv) {
    226     const struct option longopts[] = {{0, 0, 0, 0}};
    227 
    228     std::string inFilePath;
    229     ::android::vintf::AssembleVintf assembleVintf;
    230     int res;
    231     int optind;
    232     while ((res = getopt_long(argc, argv, "hi:o:mc:", longopts, &optind)) >= 0) {
    233         switch (res) {
    234             case 'i': {
    235                 inFilePath = optarg;
    236                 if (!assembleVintf.openInFile(optarg)) {
    237                     std::cerr << "Failed to open " << optarg << std::endl;
    238                     return 1;
    239                 }
    240             } break;
    241 
    242             case 'o': {
    243                 if (!assembleVintf.openOutFile(optarg)) {
    244                     std::cerr << "Failed to open " << optarg << std::endl;
    245                     return 1;
    246                 }
    247             } break;
    248 
    249             case 'm': {
    250                 assembleVintf.setOutputMatrix();
    251             } break;
    252 
    253             case 'c': {
    254                 if (strlen(optarg) != 0) {
    255                     if (!assembleVintf.openCheckFile(optarg)) {
    256                         std::cerr << "Failed to open " << optarg << std::endl;
    257                         return 1;
    258                     }
    259                 } else {
    260                     std::cerr << "WARNING: no compatibility check is done on "
    261                               << inFilePath << std::endl;
    262                 }
    263             } break;
    264 
    265             case 'h':
    266             default: {
    267                 help();
    268                 return 1;
    269             } break;
    270         }
    271     }
    272 
    273     bool success = assembleVintf.assemble();
    274 
    275     return success ? 0 : 1;
    276 }
    277 
    278