blob: cbfc368f0320c827ae6db40109538a88fe104fc2 [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
Yifan Hong79efa8a2017-07-06 14:10:28 -070027#include <vintf/KernelConfigParser.h>
Yifan Hong4d18bcc2017-04-07 21:47:16 +000028#include <vintf/parse_string.h>
29#include <vintf/parse_xml.h>
30
Yifan Hong79efa8a2017-07-06 14:10:28 -070031#define BUFFER_SIZE sysconf(_SC_PAGESIZE)
32
Yifan Hong4d18bcc2017-04-07 21:47:16 +000033namespace android {
34namespace vintf {
35
36/**
37 * Slurps the device manifest file and add build time flag to it.
38 */
39class AssembleVintf {
40public:
41 template<typename T>
42 static bool getFlag(const std::string& key, T* value) {
43 const char *envValue = getenv(key.c_str());
44 if (envValue == NULL) {
Yifan Hong488e16a2017-07-11 13:50:41 -070045 std::cerr << "Warning: " << key << " is missing, defaulted to " << (*value)
46 << std::endl;
47 return true;
Yifan Hong4d18bcc2017-04-07 21:47:16 +000048 }
49
50 if (!parse(envValue, value)) {
51 std::cerr << "Cannot parse " << envValue << "." << std::endl;
52 return false;
53 }
54 return true;
55 }
56
Yifan Hong4650ad82017-05-01 17:28:02 -070057 static std::string read(std::basic_istream<char>& is) {
58 std::stringstream ss;
59 ss << is.rdbuf();
60 return ss.str();
61 }
62
Yifan Hong79efa8a2017-07-06 14:10:28 -070063 static bool parseFileForKernelConfigs(const std::string& path, std::vector<KernelConfig>* out) {
64 std::ifstream ifs{path};
65 if (!ifs.is_open()) {
66 std::cerr << "File '" << path << "' does not exist or cannot be read." << std::endl;
67 return false;
68 }
Yifan Hong02e94002017-07-10 15:41:56 -070069 KernelConfigParser parser(true /* processComments */, true /* relaxedFormat */);
Yifan Hong79efa8a2017-07-06 14:10:28 -070070 std::string content = read(ifs);
71 status_t err = parser.process(content.c_str(), content.size());
72 if (err != OK) {
Yifan Hongae53a0e2017-07-07 15:19:06 -070073 std::cerr << parser.error();
Yifan Hong79efa8a2017-07-06 14:10:28 -070074 return false;
75 }
76 err = parser.finish();
77 if (err != OK) {
Yifan Hongae53a0e2017-07-07 15:19:06 -070078 std::cerr << parser.error();
Yifan Hong79efa8a2017-07-06 14:10:28 -070079 return false;
80 }
81
82 for (auto& configPair : parser.configs()) {
83 out->push_back({});
84 KernelConfig& config = out->back();
85 config.first = std::move(configPair.first);
86 if (!parseKernelConfigTypedValue(configPair.second, &config.second)) {
87 std::cerr << "Unknown value type for key = '" << config.first << "', value = '"
88 << configPair.second << "'\n";
89 return false;
90 }
91 }
92 return true;
93 }
94
Steve Muckle0bef8682017-07-31 15:47:15 -070095 static bool parseFilesForKernelConfigs(const std::string& path, std::vector<KernelConfig>* out) {
96 bool ret = true;
97 char *pathIter;
98 char *modPath = new char[path.length() + 1];
99 strcpy(modPath, path.c_str());
100 pathIter = strtok(modPath, ":");
101 while (ret && pathIter != NULL) {
102 ret &= parseFileForKernelConfigs(pathIter, out);
103 pathIter = strtok(NULL, ":");
104 }
105 return ret;
106 }
107
Yifan Hong9aa63702017-05-16 16:37:50 -0700108 std::basic_ostream<char>& out() const {
109 return mOutFileRef == nullptr ? std::cout : *mOutFileRef;
110 }
111
112 bool assembleHalManifest(HalManifest* halManifest) {
Yifan Hong4650ad82017-05-01 17:28:02 -0700113 std::string error;
Yifan Hong9aa63702017-05-16 16:37:50 -0700114
115 if (halManifest->mType == SchemaType::DEVICE) {
116 if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) {
117 return false;
118 }
119 }
120
121 if (mOutputMatrix) {
122 CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix();
123 if (!halManifest->checkCompatibility(generatedMatrix, &error)) {
124 std::cerr << "FATAL ERROR: cannot generate a compatible matrix: " << error
125 << std::endl;
126 }
127 out() << "<!-- \n"
128 " Autogenerated skeleton compatibility matrix. \n"
129 " Use with caution. Modify it to suit your needs.\n"
130 " All HALs are set to optional.\n"
131 " Many entries other than HALs are zero-filled and\n"
132 " require human attention. \n"
133 "-->\n"
134 << gCompatibilityMatrixConverter(generatedMatrix);
135 } else {
136 out() << gHalManifestConverter(*halManifest);
137 }
138 out().flush();
139
140 if (mCheckFile.is_open()) {
141 CompatibilityMatrix checkMatrix;
142 if (!gCompatibilityMatrixConverter(&checkMatrix, read(mCheckFile))) {
143 std::cerr << "Cannot parse check file as a compatibility matrix: "
144 << gCompatibilityMatrixConverter.lastError() << std::endl;
145 return false;
146 }
147 if (!halManifest->checkCompatibility(checkMatrix, &error)) {
148 std::cerr << "Not compatible: " << error << std::endl;
149 return false;
150 }
151 }
152
153 return true;
154 }
155
156 bool assembleCompatibilityMatrix(CompatibilityMatrix* matrix) {
157 std::string error;
158
159 KernelSepolicyVersion kernelSepolicyVers;
160 Version sepolicyVers;
161 if (matrix->mType == SchemaType::FRAMEWORK) {
162 if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) {
163 return false;
164 }
165 if (!getFlag("POLICYVERS", &kernelSepolicyVers)) {
166 return false;
167 }
Yifan Hong79efa8a2017-07-06 14:10:28 -0700168 for (const auto& pair : mKernels) {
169 std::vector<KernelConfig> configs;
Steve Muckle0bef8682017-07-31 15:47:15 -0700170 if (!parseFilesForKernelConfigs(pair.second, &configs)) {
Yifan Hong79efa8a2017-07-06 14:10:28 -0700171 return false;
172 }
173 bool added = false;
174 for (auto& e : matrix->framework.mKernels) {
175 if (e.minLts() == pair.first) {
176 e.mConfigs.insert(e.mConfigs.end(), configs.begin(), configs.end());
177 added = true;
178 break;
179 }
180 }
181 if (!added) {
182 matrix->framework.mKernels.push_back(
183 MatrixKernel{KernelVersion{pair.first}, std::move(configs)});
184 }
185 }
Yifan Hong9aa63702017-05-16 16:37:50 -0700186 matrix->framework.mSepolicy =
187 Sepolicy(kernelSepolicyVers, {{sepolicyVers.majorVer, sepolicyVers.minorVer}});
Yifan Hong7f6c00c2017-07-06 19:50:29 +0000188
189 Version avbMetaVersion;
190 if (!getFlag("FRAMEWORK_VBMETA_VERSION", &avbMetaVersion)) {
191 return false;
192 }
193 matrix->framework.mAvbMetaVersion = avbMetaVersion;
Yifan Hong9aa63702017-05-16 16:37:50 -0700194 }
195 out() << gCompatibilityMatrixConverter(*matrix);
196 out().flush();
197
198 if (mCheckFile.is_open()) {
199 HalManifest checkManifest;
200 if (!gHalManifestConverter(&checkManifest, read(mCheckFile))) {
201 std::cerr << "Cannot parse check file as a HAL manifest: "
202 << gHalManifestConverter.lastError() << std::endl;
203 return false;
204 }
205 if (!checkManifest.checkCompatibility(*matrix, &error)) {
206 std::cerr << "Not compatible: " << error << std::endl;
207 return false;
208 }
209 }
210
211 return true;
212 }
213
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700214 enum AssembleStatus { SUCCESS, FAIL_AND_EXIT, TRY_NEXT };
215 template <typename Schema, typename AssembleFunc>
216 AssembleStatus tryAssemble(const XmlConverter<Schema>& converter, const std::string& schemaName,
217 AssembleFunc assemble) {
218 Schema schema;
219 if (!converter(&schema, read(mInFiles.front()))) {
220 return TRY_NEXT;
221 }
222 auto firstType = schema.type();
223 for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) {
224 Schema additionalSchema;
225 if (!converter(&additionalSchema, read(*it))) {
226 std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
227 << "\" is not a valid " << firstType << " " << schemaName
228 << " (but the first file is a valid " << firstType << " " << schemaName
229 << "). Error: " << converter.lastError() << std::endl;
230 return FAIL_AND_EXIT;
231 }
232 if (additionalSchema.type() != firstType) {
233 std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
234 << "\" is a " << additionalSchema.type() << " " << schemaName
235 << " (but a " << firstType << " " << schemaName << " is expected)."
236 << std::endl;
237 return FAIL_AND_EXIT;
238 }
239 schema.addAll(std::move(additionalSchema));
240 }
241 return assemble(&schema) ? SUCCESS : FAIL_AND_EXIT;
242 }
243
Yifan Hong9aa63702017-05-16 16:37:50 -0700244 bool assemble() {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700245 using std::placeholders::_1;
246 if (mInFiles.empty()) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700247 std::cerr << "Missing input file." << std::endl;
248 return false;
249 }
250
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700251 auto status = tryAssemble(gHalManifestConverter, "manifest",
252 std::bind(&AssembleVintf::assembleHalManifest, this, _1));
253 if (status == SUCCESS) return true;
254 if (status == FAIL_AND_EXIT) return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000255
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700256 resetInFiles();
Yifan Honga59d2562017-04-18 18:01:16 -0700257
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700258 status = tryAssemble(gCompatibilityMatrixConverter, "compatibility matrix",
259 std::bind(&AssembleVintf::assembleCompatibilityMatrix, this, _1));
260 if (status == SUCCESS) return true;
261 if (status == FAIL_AND_EXIT) return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000262
Yifan Hong959ee1b2017-04-28 14:37:56 -0700263 std::cerr << "Input file has unknown format." << std::endl
264 << "Error when attempting to convert to manifest: "
265 << gHalManifestConverter.lastError() << std::endl
266 << "Error when attempting to convert to compatibility matrix: "
267 << gCompatibilityMatrixConverter.lastError() << std::endl;
268 return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000269 }
Yifan Hong9aa63702017-05-16 16:37:50 -0700270
271 bool openOutFile(const char* path) {
272 mOutFileRef = std::make_unique<std::ofstream>();
273 mOutFileRef->open(path);
274 return mOutFileRef->is_open();
275 }
276
277 bool openInFile(const char* path) {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700278 mInFilePaths.push_back(path);
279 mInFiles.push_back({});
280 mInFiles.back().open(path);
281 return mInFiles.back().is_open();
Yifan Hong9aa63702017-05-16 16:37:50 -0700282 }
283
284 bool openCheckFile(const char* path) {
285 mCheckFile.open(path);
286 return mCheckFile.is_open();
287 }
288
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700289 void resetInFiles() {
290 for (auto& inFile : mInFiles) {
291 inFile.clear();
292 inFile.seekg(0);
293 }
294 }
295
Yifan Hong9aa63702017-05-16 16:37:50 -0700296 void setOutputMatrix() { mOutputMatrix = true; }
297
Yifan Hong79efa8a2017-07-06 14:10:28 -0700298 bool addKernel(const std::string& kernelArg) {
299 auto ind = kernelArg.find(':');
300 if (ind == std::string::npos) {
301 std::cerr << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl;
302 return false;
303 }
304 std::string kernelVerStr{kernelArg.begin(), kernelArg.begin() + ind};
305 std::string kernelConfigPath{kernelArg.begin() + ind + 1, kernelArg.end()};
306 Version kernelVer;
307 if (!parse(kernelVerStr, &kernelVer)) {
308 std::cerr << "Unrecognized kernel version '" << kernelVerStr << "'" << std::endl;
309 return false;
310 }
311 mKernels.push_back({{kernelVer.majorVer, kernelVer.minorVer, 0u}, kernelConfigPath});
312 return true;
313 }
314
Yifan Hong9aa63702017-05-16 16:37:50 -0700315 private:
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700316 std::vector<std::string> mInFilePaths;
317 std::vector<std::ifstream> mInFiles;
Yifan Hong9aa63702017-05-16 16:37:50 -0700318 std::unique_ptr<std::ofstream> mOutFileRef;
319 std::ifstream mCheckFile;
320 bool mOutputMatrix = false;
Yifan Hong79efa8a2017-07-06 14:10:28 -0700321 std::vector<std::pair<KernelVersion, std::string>> mKernels;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000322};
323
324} // namespace vintf
325} // namespace android
326
327void help() {
Yifan Hong9aa63702017-05-16 16:37:50 -0700328 std::cerr << "assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
329 " fill in build-time flags into the given file.\n"
330 "assemble_vintf -h\n"
331 " Display this help text.\n"
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700332 "assemble_vintf -i <input file>[:<input file>[...]] [-o <output file>] [-m]\n"
333 " [-c [<check file>]]\n"
Yifan Hong9aa63702017-05-16 16:37:50 -0700334 " Fill in build-time flags into the given file.\n"
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700335 " -i <input file>[:<input file>[...]]\n"
336 " A list of input files. Format is automatically detected for the\n"
337 " first file, and the remaining files must have the same format.\n"
338 " Files other than the first file should only have <hal> defined;\n"
339 " other entries are ignored.\n"
Yifan Hong9aa63702017-05-16 16:37:50 -0700340 " -o <output file>\n"
341 " Optional output file. If not specified, write to stdout.\n"
342 " -m\n"
343 " a compatible compatibility matrix is\n"
344 " generated instead; for example, given a device manifest,\n"
345 " a framework compatibility matrix is generated. This flag\n"
346 " is ignored when input is a compatibility matrix.\n"
347 " -c [<check file>]\n"
348 " After writing the output file, check compatibility between\n"
349 " output file and check file.\n"
350 " If -c is set but the check file is not specified, a warning\n"
351 " message is written to stderr. Return 0.\n"
352 " If the check file is specified but is not compatible, an error\n"
Yifan Hong79efa8a2017-07-06 14:10:28 -0700353 " message is written to stderr. Return 1.\n"
Steve Muckle0bef8682017-07-31 15:47:15 -0700354 " --kernel=<version>:<android-base.cfg>[:<android-base-arch.cfg>[...]]\n"
Yifan Hong79efa8a2017-07-06 14:10:28 -0700355 " Add a kernel entry to framework compatibility matrix.\n"
356 " Ignored for other input format.\n"
357 " <version> has format: 3.18\n"
Steve Muckle0bef8682017-07-31 15:47:15 -0700358 " <android-base.cfg> is the location of android-base.cfg\n"
359 " <android-base-arch.cfg> is the location of an optional\n"
360 " arch-specific config fragment, more than one may be specified\n";
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000361}
362
363int main(int argc, char **argv) {
Yifan Hong79efa8a2017-07-06 14:10:28 -0700364 const struct option longopts[] = {{"kernel", required_argument, NULL, 'k'}, {0, 0, 0, 0}};
Yifan Hong9aa63702017-05-16 16:37:50 -0700365
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700366 std::string outFilePath;
Yifan Hong9aa63702017-05-16 16:37:50 -0700367 ::android::vintf::AssembleVintf assembleVintf;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000368 int res;
Yifan Hong9aa63702017-05-16 16:37:50 -0700369 int optind;
370 while ((res = getopt_long(argc, argv, "hi:o:mc:", longopts, &optind)) >= 0) {
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000371 switch (res) {
372 case 'i': {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700373 char* inFilePath = strtok(optarg, ":");
374 while (inFilePath != NULL) {
375 if (!assembleVintf.openInFile(inFilePath)) {
376 std::cerr << "Failed to open " << optarg << std::endl;
377 return 1;
378 }
379 inFilePath = strtok(NULL, ":");
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000380 }
381 } break;
382
383 case 'o': {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700384 outFilePath = optarg;
Yifan Hong9aa63702017-05-16 16:37:50 -0700385 if (!assembleVintf.openOutFile(optarg)) {
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000386 std::cerr << "Failed to open " << optarg << std::endl;
387 return 1;
388 }
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000389 } break;
390
Yifan Honga59d2562017-04-18 18:01:16 -0700391 case 'm': {
Yifan Hong9aa63702017-05-16 16:37:50 -0700392 assembleVintf.setOutputMatrix();
Yifan Honga59d2562017-04-18 18:01:16 -0700393 } break;
394
Yifan Hong4650ad82017-05-01 17:28:02 -0700395 case 'c': {
396 if (strlen(optarg) != 0) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700397 if (!assembleVintf.openCheckFile(optarg)) {
Yifan Hong4650ad82017-05-01 17:28:02 -0700398 std::cerr << "Failed to open " << optarg << std::endl;
399 return 1;
400 }
401 } else {
402 std::cerr << "WARNING: no compatibility check is done on "
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700403 << (outFilePath.empty() ? "output" : outFilePath) << std::endl;
Yifan Hong4650ad82017-05-01 17:28:02 -0700404 }
405 } break;
406
Yifan Hong79efa8a2017-07-06 14:10:28 -0700407 case 'k': {
408 if (!assembleVintf.addKernel(optarg)) {
409 std::cerr << "ERROR: Unrecognized --kernel argument." << std::endl;
410 return 1;
411 }
412 } break;
413
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000414 case 'h':
415 default: {
416 help();
417 return 1;
418 } break;
419 }
420 }
421
Yifan Hong9aa63702017-05-16 16:37:50 -0700422 bool success = assembleVintf.assemble();
Yifan Hong4650ad82017-05-01 17:28:02 -0700423
424 return success ? 0 : 1;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000425}
426