blob: 98fdae3da8c8164b25e565c597ac736e927ffb3a [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) {
45 std::cerr << "Required " << key << " flag." << std::endl;
46 return false;
47 }
48
49 if (!parse(envValue, value)) {
50 std::cerr << "Cannot parse " << envValue << "." << std::endl;
51 return false;
52 }
53 return true;
54 }
55
Yifan Hong4650ad82017-05-01 17:28:02 -070056 static std::string read(std::basic_istream<char>& is) {
57 std::stringstream ss;
58 ss << is.rdbuf();
59 return ss.str();
60 }
61
Yifan Hong79efa8a2017-07-06 14:10:28 -070062 static bool parseFileForKernelConfigs(const std::string& path, std::vector<KernelConfig>* out) {
63 std::ifstream ifs{path};
64 if (!ifs.is_open()) {
65 std::cerr << "File '" << path << "' does not exist or cannot be read." << std::endl;
66 return false;
67 }
Yifan Hong02e94002017-07-10 15:41:56 -070068 KernelConfigParser parser(true /* processComments */, true /* relaxedFormat */);
Yifan Hong79efa8a2017-07-06 14:10:28 -070069 std::string content = read(ifs);
70 status_t err = parser.process(content.c_str(), content.size());
71 if (err != OK) {
Yifan Hongae53a0e2017-07-07 15:19:06 -070072 std::cerr << parser.error();
Yifan Hong79efa8a2017-07-06 14:10:28 -070073 return false;
74 }
75 err = parser.finish();
76 if (err != OK) {
Yifan Hongae53a0e2017-07-07 15:19:06 -070077 std::cerr << parser.error();
Yifan Hong79efa8a2017-07-06 14:10:28 -070078 return false;
79 }
80
81 for (auto& configPair : parser.configs()) {
82 out->push_back({});
83 KernelConfig& config = out->back();
84 config.first = std::move(configPair.first);
85 if (!parseKernelConfigTypedValue(configPair.second, &config.second)) {
86 std::cerr << "Unknown value type for key = '" << config.first << "', value = '"
87 << configPair.second << "'\n";
88 return false;
89 }
90 }
91 return true;
92 }
93
Yifan Hong9aa63702017-05-16 16:37:50 -070094 std::basic_ostream<char>& out() const {
95 return mOutFileRef == nullptr ? std::cout : *mOutFileRef;
96 }
97
98 bool assembleHalManifest(HalManifest* halManifest) {
Yifan Hong4650ad82017-05-01 17:28:02 -070099 std::string error;
Yifan Hong9aa63702017-05-16 16:37:50 -0700100
101 if (halManifest->mType == SchemaType::DEVICE) {
102 if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) {
103 return false;
104 }
105 }
106
107 if (mOutputMatrix) {
108 CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix();
109 if (!halManifest->checkCompatibility(generatedMatrix, &error)) {
110 std::cerr << "FATAL ERROR: cannot generate a compatible matrix: " << error
111 << std::endl;
112 }
113 out() << "<!-- \n"
114 " Autogenerated skeleton compatibility matrix. \n"
115 " Use with caution. Modify it to suit your needs.\n"
116 " All HALs are set to optional.\n"
117 " Many entries other than HALs are zero-filled and\n"
118 " require human attention. \n"
119 "-->\n"
120 << gCompatibilityMatrixConverter(generatedMatrix);
121 } else {
122 out() << gHalManifestConverter(*halManifest);
123 }
124 out().flush();
125
126 if (mCheckFile.is_open()) {
127 CompatibilityMatrix checkMatrix;
128 if (!gCompatibilityMatrixConverter(&checkMatrix, read(mCheckFile))) {
129 std::cerr << "Cannot parse check file as a compatibility matrix: "
130 << gCompatibilityMatrixConverter.lastError() << std::endl;
131 return false;
132 }
133 if (!halManifest->checkCompatibility(checkMatrix, &error)) {
134 std::cerr << "Not compatible: " << error << std::endl;
135 return false;
136 }
137 }
138
139 return true;
140 }
141
142 bool assembleCompatibilityMatrix(CompatibilityMatrix* matrix) {
143 std::string error;
144
145 KernelSepolicyVersion kernelSepolicyVers;
146 Version sepolicyVers;
147 if (matrix->mType == SchemaType::FRAMEWORK) {
148 if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) {
149 return false;
150 }
151 if (!getFlag("POLICYVERS", &kernelSepolicyVers)) {
152 return false;
153 }
Yifan Hong79efa8a2017-07-06 14:10:28 -0700154 for (const auto& pair : mKernels) {
155 std::vector<KernelConfig> configs;
156 if (!parseFileForKernelConfigs(pair.second, &configs)) {
157 return false;
158 }
159 bool added = false;
160 for (auto& e : matrix->framework.mKernels) {
161 if (e.minLts() == pair.first) {
162 e.mConfigs.insert(e.mConfigs.end(), configs.begin(), configs.end());
163 added = true;
164 break;
165 }
166 }
167 if (!added) {
168 matrix->framework.mKernels.push_back(
169 MatrixKernel{KernelVersion{pair.first}, std::move(configs)});
170 }
171 }
Yifan Hong9aa63702017-05-16 16:37:50 -0700172 matrix->framework.mSepolicy =
173 Sepolicy(kernelSepolicyVers, {{sepolicyVers.majorVer, sepolicyVers.minorVer}});
Yifan Hong7f6c00c2017-07-06 19:50:29 +0000174
175 Version avbMetaVersion;
176 if (!getFlag("FRAMEWORK_VBMETA_VERSION", &avbMetaVersion)) {
177 return false;
178 }
179 matrix->framework.mAvbMetaVersion = avbMetaVersion;
Yifan Hong9aa63702017-05-16 16:37:50 -0700180 }
181 out() << gCompatibilityMatrixConverter(*matrix);
182 out().flush();
183
184 if (mCheckFile.is_open()) {
185 HalManifest checkManifest;
186 if (!gHalManifestConverter(&checkManifest, read(mCheckFile))) {
187 std::cerr << "Cannot parse check file as a HAL manifest: "
188 << gHalManifestConverter.lastError() << std::endl;
189 return false;
190 }
191 if (!checkManifest.checkCompatibility(*matrix, &error)) {
192 std::cerr << "Not compatible: " << error << std::endl;
193 return false;
194 }
195 }
196
197 return true;
198 }
199
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700200 enum AssembleStatus { SUCCESS, FAIL_AND_EXIT, TRY_NEXT };
201 template <typename Schema, typename AssembleFunc>
202 AssembleStatus tryAssemble(const XmlConverter<Schema>& converter, const std::string& schemaName,
203 AssembleFunc assemble) {
204 Schema schema;
205 if (!converter(&schema, read(mInFiles.front()))) {
206 return TRY_NEXT;
207 }
208 auto firstType = schema.type();
209 for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) {
210 Schema additionalSchema;
211 if (!converter(&additionalSchema, read(*it))) {
212 std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
213 << "\" is not a valid " << firstType << " " << schemaName
214 << " (but the first file is a valid " << firstType << " " << schemaName
215 << "). Error: " << converter.lastError() << std::endl;
216 return FAIL_AND_EXIT;
217 }
218 if (additionalSchema.type() != firstType) {
219 std::cerr << "File \"" << mInFilePaths[std::distance(mInFiles.begin(), it)]
220 << "\" is a " << additionalSchema.type() << " " << schemaName
221 << " (but a " << firstType << " " << schemaName << " is expected)."
222 << std::endl;
223 return FAIL_AND_EXIT;
224 }
225 schema.addAll(std::move(additionalSchema));
226 }
227 return assemble(&schema) ? SUCCESS : FAIL_AND_EXIT;
228 }
229
Yifan Hong9aa63702017-05-16 16:37:50 -0700230 bool assemble() {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700231 using std::placeholders::_1;
232 if (mInFiles.empty()) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700233 std::cerr << "Missing input file." << std::endl;
234 return false;
235 }
236
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700237 auto status = tryAssemble(gHalManifestConverter, "manifest",
238 std::bind(&AssembleVintf::assembleHalManifest, this, _1));
239 if (status == SUCCESS) return true;
240 if (status == FAIL_AND_EXIT) return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000241
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700242 resetInFiles();
Yifan Honga59d2562017-04-18 18:01:16 -0700243
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700244 status = tryAssemble(gCompatibilityMatrixConverter, "compatibility matrix",
245 std::bind(&AssembleVintf::assembleCompatibilityMatrix, this, _1));
246 if (status == SUCCESS) return true;
247 if (status == FAIL_AND_EXIT) return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000248
Yifan Hong959ee1b2017-04-28 14:37:56 -0700249 std::cerr << "Input file has unknown format." << std::endl
250 << "Error when attempting to convert to manifest: "
251 << gHalManifestConverter.lastError() << std::endl
252 << "Error when attempting to convert to compatibility matrix: "
253 << gCompatibilityMatrixConverter.lastError() << std::endl;
254 return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000255 }
Yifan Hong9aa63702017-05-16 16:37:50 -0700256
257 bool openOutFile(const char* path) {
258 mOutFileRef = std::make_unique<std::ofstream>();
259 mOutFileRef->open(path);
260 return mOutFileRef->is_open();
261 }
262
263 bool openInFile(const char* path) {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700264 mInFilePaths.push_back(path);
265 mInFiles.push_back({});
266 mInFiles.back().open(path);
267 return mInFiles.back().is_open();
Yifan Hong9aa63702017-05-16 16:37:50 -0700268 }
269
270 bool openCheckFile(const char* path) {
271 mCheckFile.open(path);
272 return mCheckFile.is_open();
273 }
274
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700275 void resetInFiles() {
276 for (auto& inFile : mInFiles) {
277 inFile.clear();
278 inFile.seekg(0);
279 }
280 }
281
Yifan Hong9aa63702017-05-16 16:37:50 -0700282 void setOutputMatrix() { mOutputMatrix = true; }
283
Yifan Hong79efa8a2017-07-06 14:10:28 -0700284 bool addKernel(const std::string& kernelArg) {
285 auto ind = kernelArg.find(':');
286 if (ind == std::string::npos) {
287 std::cerr << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl;
288 return false;
289 }
290 std::string kernelVerStr{kernelArg.begin(), kernelArg.begin() + ind};
291 std::string kernelConfigPath{kernelArg.begin() + ind + 1, kernelArg.end()};
292 Version kernelVer;
293 if (!parse(kernelVerStr, &kernelVer)) {
294 std::cerr << "Unrecognized kernel version '" << kernelVerStr << "'" << std::endl;
295 return false;
296 }
297 mKernels.push_back({{kernelVer.majorVer, kernelVer.minorVer, 0u}, kernelConfigPath});
298 return true;
299 }
300
Yifan Hong9aa63702017-05-16 16:37:50 -0700301 private:
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700302 std::vector<std::string> mInFilePaths;
303 std::vector<std::ifstream> mInFiles;
Yifan Hong9aa63702017-05-16 16:37:50 -0700304 std::unique_ptr<std::ofstream> mOutFileRef;
305 std::ifstream mCheckFile;
306 bool mOutputMatrix = false;
Yifan Hong79efa8a2017-07-06 14:10:28 -0700307 std::vector<std::pair<KernelVersion, std::string>> mKernels;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000308};
309
310} // namespace vintf
311} // namespace android
312
313void help() {
Yifan Hong9aa63702017-05-16 16:37:50 -0700314 std::cerr << "assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
315 " fill in build-time flags into the given file.\n"
316 "assemble_vintf -h\n"
317 " Display this help text.\n"
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700318 "assemble_vintf -i <input file>[:<input file>[...]] [-o <output file>] [-m]\n"
319 " [-c [<check file>]]\n"
Yifan Hong9aa63702017-05-16 16:37:50 -0700320 " Fill in build-time flags into the given file.\n"
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700321 " -i <input file>[:<input file>[...]]\n"
322 " A list of input files. Format is automatically detected for the\n"
323 " first file, and the remaining files must have the same format.\n"
324 " Files other than the first file should only have <hal> defined;\n"
325 " other entries are ignored.\n"
Yifan Hong9aa63702017-05-16 16:37:50 -0700326 " -o <output file>\n"
327 " Optional output file. If not specified, write to stdout.\n"
328 " -m\n"
329 " a compatible compatibility matrix is\n"
330 " generated instead; for example, given a device manifest,\n"
331 " a framework compatibility matrix is generated. This flag\n"
332 " is ignored when input is a compatibility matrix.\n"
333 " -c [<check file>]\n"
334 " After writing the output file, check compatibility between\n"
335 " output file and check file.\n"
336 " If -c is set but the check file is not specified, a warning\n"
337 " message is written to stderr. Return 0.\n"
338 " If the check file is specified but is not compatible, an error\n"
Yifan Hong79efa8a2017-07-06 14:10:28 -0700339 " message is written to stderr. Return 1.\n"
340 " --kernel=<version>:<android-base.cfg>\n"
341 " Add a kernel entry to framework compatibility matrix.\n"
342 " Ignored for other input format.\n"
343 " <version> has format: 3.18\n"
344 " <android-base.cfg> is the location of android-base.cfg\n";
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000345}
346
347int main(int argc, char **argv) {
Yifan Hong79efa8a2017-07-06 14:10:28 -0700348 const struct option longopts[] = {{"kernel", required_argument, NULL, 'k'}, {0, 0, 0, 0}};
Yifan Hong9aa63702017-05-16 16:37:50 -0700349
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700350 std::string outFilePath;
Yifan Hong9aa63702017-05-16 16:37:50 -0700351 ::android::vintf::AssembleVintf assembleVintf;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000352 int res;
Yifan Hong9aa63702017-05-16 16:37:50 -0700353 int optind;
354 while ((res = getopt_long(argc, argv, "hi:o:mc:", longopts, &optind)) >= 0) {
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000355 switch (res) {
356 case 'i': {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700357 char* inFilePath = strtok(optarg, ":");
358 while (inFilePath != NULL) {
359 if (!assembleVintf.openInFile(inFilePath)) {
360 std::cerr << "Failed to open " << optarg << std::endl;
361 return 1;
362 }
363 inFilePath = strtok(NULL, ":");
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000364 }
365 } break;
366
367 case 'o': {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700368 outFilePath = optarg;
Yifan Hong9aa63702017-05-16 16:37:50 -0700369 if (!assembleVintf.openOutFile(optarg)) {
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000370 std::cerr << "Failed to open " << optarg << std::endl;
371 return 1;
372 }
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000373 } break;
374
Yifan Honga59d2562017-04-18 18:01:16 -0700375 case 'm': {
Yifan Hong9aa63702017-05-16 16:37:50 -0700376 assembleVintf.setOutputMatrix();
Yifan Honga59d2562017-04-18 18:01:16 -0700377 } break;
378
Yifan Hong4650ad82017-05-01 17:28:02 -0700379 case 'c': {
380 if (strlen(optarg) != 0) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700381 if (!assembleVintf.openCheckFile(optarg)) {
Yifan Hong4650ad82017-05-01 17:28:02 -0700382 std::cerr << "Failed to open " << optarg << std::endl;
383 return 1;
384 }
385 } else {
386 std::cerr << "WARNING: no compatibility check is done on "
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700387 << (outFilePath.empty() ? "output" : outFilePath) << std::endl;
Yifan Hong4650ad82017-05-01 17:28:02 -0700388 }
389 } break;
390
Yifan Hong79efa8a2017-07-06 14:10:28 -0700391 case 'k': {
392 if (!assembleVintf.addKernel(optarg)) {
393 std::cerr << "ERROR: Unrecognized --kernel argument." << std::endl;
394 return 1;
395 }
396 } break;
397
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000398 case 'h':
399 default: {
400 help();
401 return 1;
402 } break;
403 }
404 }
405
Yifan Hong9aa63702017-05-16 16:37:50 -0700406 bool success = assembleVintf.assemble();
Yifan Hong4650ad82017-05-01 17:28:02 -0700407
408 return success ? 0 : 1;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000409}
410