blob: 62d65b07473536b5d1a2d33eea0e073ae5839759 [file] [log] [blame]
Yifan Hong4d18bcc2017-04-07 21:47:16 +00001/*
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
Yifan Hong9aa63702017-05-16 16:37:50 -070017#include <getopt.h>
Yifan Hong4d18bcc2017-04-07 21:47:16 +000018#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
30namespace android {
31namespace vintf {
32
33/**
34 * Slurps the device manifest file and add build time flag to it.
35 */
36class AssembleVintf {
37public:
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
Yifan Hong4650ad82017-05-01 17:28:02 -070053 static std::string read(std::basic_istream<char>& is) {
54 std::stringstream ss;
55 ss << is.rdbuf();
56 return ss.str();
57 }
58
Yifan Hong9aa63702017-05-16 16:37:50 -070059 std::basic_ostream<char>& out() const {
60 return mOutFileRef == nullptr ? std::cout : *mOutFileRef;
61 }
62
63 bool assembleHalManifest(HalManifest* halManifest) {
Yifan Hong4650ad82017-05-01 17:28:02 -070064 std::string error;
Yifan Hong9aa63702017-05-16 16:37:50 -070065
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
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700141 enum AssembleStatus { SUCCESS, FAIL_AND_EXIT, TRY_NEXT };
142 template <typename Schema, typename AssembleFunc>
143 AssembleStatus tryAssemble(const XmlConverter<Schema>& converter, const std::string& schemaName,
144 AssembleFunc assemble) {
145 Schema schema;
146 if (!converter(&schema, read(mInFiles.front()))) {
147 return TRY_NEXT;
148 }
149 auto firstType = schema.type();
150 for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) {
151 Schema additionalSchema;
152 if (!converter(&additionalSchema, read(*it))) {
153 std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
154 << "\" is not a valid " << firstType << " " << schemaName
155 << " (but the first file is a valid " << firstType << " " << schemaName
156 << "). Error: " << converter.lastError() << std::endl;
157 return FAIL_AND_EXIT;
158 }
159 if (additionalSchema.type() != firstType) {
160 std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
161 << "\" is a " << additionalSchema.type() << " " << schemaName
162 << " (but a " << firstType << " " << schemaName << " is expected)."
163 << std::endl;
164 return FAIL_AND_EXIT;
165 }
166 schema.addAll(std::move(additionalSchema));
167 }
168 return assemble(&schema) ? SUCCESS : FAIL_AND_EXIT;
169 }
170
Yifan Hong9aa63702017-05-16 16:37:50 -0700171 bool assemble() {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700172 using std::placeholders::_1;
173 if (mInFiles.empty()) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700174 std::cerr << "Missing input file." << std::endl;
175 return false;
176 }
177
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700178 auto status = tryAssemble(gHalManifestConverter, "manifest",
179 std::bind(&AssembleVintf::assembleHalManifest, this, _1));
180 if (status == SUCCESS) return true;
181 if (status == FAIL_AND_EXIT) return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000182
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700183 resetInFiles();
Yifan Honga59d2562017-04-18 18:01:16 -0700184
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700185 status = tryAssemble(gCompatibilityMatrixConverter, "compatibility matrix",
186 std::bind(&AssembleVintf::assembleCompatibilityMatrix, this, _1));
187 if (status == SUCCESS) return true;
188 if (status == FAIL_AND_EXIT) return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000189
Yifan Hong959ee1b2017-04-28 14:37:56 -0700190 std::cerr << "Input file has unknown format." << std::endl
191 << "Error when attempting to convert to manifest: "
192 << gHalManifestConverter.lastError() << std::endl
193 << "Error when attempting to convert to compatibility matrix: "
194 << gCompatibilityMatrixConverter.lastError() << std::endl;
195 return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000196 }
Yifan Hong9aa63702017-05-16 16:37:50 -0700197
198 bool openOutFile(const char* path) {
199 mOutFileRef = std::make_unique<std::ofstream>();
200 mOutFileRef->open(path);
201 return mOutFileRef->is_open();
202 }
203
204 bool openInFile(const char* path) {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700205 mInFilePaths.push_back(path);
206 mInFiles.push_back({});
207 mInFiles.back().open(path);
208 return mInFiles.back().is_open();
Yifan Hong9aa63702017-05-16 16:37:50 -0700209 }
210
211 bool openCheckFile(const char* path) {
212 mCheckFile.open(path);
213 return mCheckFile.is_open();
214 }
215
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700216 void resetInFiles() {
217 for (auto& inFile : mInFiles) {
218 inFile.clear();
219 inFile.seekg(0);
220 }
221 }
222
Yifan Hong9aa63702017-05-16 16:37:50 -0700223 void setOutputMatrix() { mOutputMatrix = true; }
224
225 private:
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700226 std::vector<std::string> mInFilePaths;
227 std::vector<std::ifstream> mInFiles;
Yifan Hong9aa63702017-05-16 16:37:50 -0700228 std::unique_ptr<std::ofstream> mOutFileRef;
229 std::ifstream mCheckFile;
230 bool mOutputMatrix = false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000231};
232
233} // namespace vintf
234} // namespace android
235
236void help() {
Yifan Hong9aa63702017-05-16 16:37:50 -0700237 std::cerr << "assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
238 " fill in build-time flags into the given file.\n"
239 "assemble_vintf -h\n"
240 " Display this help text.\n"
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700241 "assemble_vintf -i <input file>[:<input file>[...]] [-o <output file>] [-m]\n"
242 " [-c [<check file>]]\n"
Yifan Hong9aa63702017-05-16 16:37:50 -0700243 " Fill in build-time flags into the given file.\n"
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700244 " -i <input file>[:<input file>[...]]\n"
245 " A list of input files. Format is automatically detected for the\n"
246 " first file, and the remaining files must have the same format.\n"
247 " Files other than the first file should only have <hal> defined;\n"
248 " other entries are ignored.\n"
Yifan Hong9aa63702017-05-16 16:37:50 -0700249 " -o <output file>\n"
250 " Optional output file. If not specified, write to stdout.\n"
251 " -m\n"
252 " a compatible compatibility matrix is\n"
253 " generated instead; for example, given a device manifest,\n"
254 " a framework compatibility matrix is generated. This flag\n"
255 " is ignored when input is a compatibility matrix.\n"
256 " -c [<check file>]\n"
257 " After writing the output file, check compatibility between\n"
258 " output file and check file.\n"
259 " If -c is set but the check file is not specified, a warning\n"
260 " message is written to stderr. Return 0.\n"
261 " If the check file is specified but is not compatible, an error\n"
262 " message is written to stderr. Return 1.\n";
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000263}
264
265int main(int argc, char **argv) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700266 const struct option longopts[] = {{0, 0, 0, 0}};
267
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700268 std::string outFilePath;
Yifan Hong9aa63702017-05-16 16:37:50 -0700269 ::android::vintf::AssembleVintf assembleVintf;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000270 int res;
Yifan Hong9aa63702017-05-16 16:37:50 -0700271 int optind;
272 while ((res = getopt_long(argc, argv, "hi:o:mc:", longopts, &optind)) >= 0) {
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000273 switch (res) {
274 case 'i': {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700275 char* inFilePath = strtok(optarg, ":");
276 while (inFilePath != NULL) {
277 if (!assembleVintf.openInFile(inFilePath)) {
278 std::cerr << "Failed to open " << optarg << std::endl;
279 return 1;
280 }
281 inFilePath = strtok(NULL, ":");
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000282 }
283 } break;
284
285 case 'o': {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700286 outFilePath = optarg;
Yifan Hong9aa63702017-05-16 16:37:50 -0700287 if (!assembleVintf.openOutFile(optarg)) {
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000288 std::cerr << "Failed to open " << optarg << std::endl;
289 return 1;
290 }
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000291 } break;
292
Yifan Honga59d2562017-04-18 18:01:16 -0700293 case 'm': {
Yifan Hong9aa63702017-05-16 16:37:50 -0700294 assembleVintf.setOutputMatrix();
Yifan Honga59d2562017-04-18 18:01:16 -0700295 } break;
296
Yifan Hong4650ad82017-05-01 17:28:02 -0700297 case 'c': {
298 if (strlen(optarg) != 0) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700299 if (!assembleVintf.openCheckFile(optarg)) {
Yifan Hong4650ad82017-05-01 17:28:02 -0700300 std::cerr << "Failed to open " << optarg << std::endl;
301 return 1;
302 }
303 } else {
304 std::cerr << "WARNING: no compatibility check is done on "
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700305 << (outFilePath.empty() ? "output" : outFilePath) << std::endl;
Yifan Hong4650ad82017-05-01 17:28:02 -0700306 }
307 } break;
308
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000309 case 'h':
310 default: {
311 help();
312 return 1;
313 } break;
314 }
315 }
316
Yifan Hong9aa63702017-05-16 16:37:50 -0700317 bool success = assembleVintf.assemble();
Yifan Hong4650ad82017-05-01 17:28:02 -0700318
319 return success ? 0 : 1;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000320}
321