blob: 17f5d0cd4a745512c9c47a9af6263bcbfe22af6f [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 Hong9a8b1a72017-08-25 17:55:33 -070027#include <android-base/file.h>
Yifan Hongdbe9db32017-12-11 19:06:11 -080028#include <android-base/parseint.h>
Yifan Hong9a8b1a72017-08-25 17:55:33 -070029
Yifan Hong79efa8a2017-07-06 14:10:28 -070030#include <vintf/KernelConfigParser.h>
Yifan Hong4d18bcc2017-04-07 21:47:16 +000031#include <vintf/parse_string.h>
32#include <vintf/parse_xml.h>
33
Yifan Hong79efa8a2017-07-06 14:10:28 -070034#define BUFFER_SIZE sysconf(_SC_PAGESIZE)
35
Yifan Hong4d18bcc2017-04-07 21:47:16 +000036namespace android {
37namespace vintf {
38
Yifan Hong9a8b1a72017-08-25 17:55:33 -070039static const std::string gConfigPrefix = "android-base-";
40static const std::string gConfigSuffix = ".cfg";
41static const std::string gBaseConfig = "android-base.cfg";
42
Yifan Hongaa219f52017-12-18 18:51:59 -080043// An input stream with a name.
44// The input stream may be an actual file, or a stringstream for testing.
45// It takes ownership on the istream.
46class NamedIstream {
47 public:
48 NamedIstream(const std::string& name, std::unique_ptr<std::istream>&& stream)
49 : mName(name), mStream(std::move(stream)) {}
50 const std::string& name() const { return mName; }
51 std::istream& stream() { return *mStream; }
52
53 private:
54 std::string mName;
55 std::unique_ptr<std::istream> mStream;
56};
57
Yifan Hong4d18bcc2017-04-07 21:47:16 +000058/**
59 * Slurps the device manifest file and add build time flag to it.
60 */
61class AssembleVintf {
Yifan Hong9a8b1a72017-08-25 17:55:33 -070062 using Condition = std::unique_ptr<KernelConfig>;
63 using ConditionedConfig = std::pair<Condition, std::vector<KernelConfig> /* configs */>;
64
65 public:
Yifan Hong4d18bcc2017-04-07 21:47:16 +000066 template<typename T>
67 static bool getFlag(const std::string& key, T* value) {
68 const char *envValue = getenv(key.c_str());
69 if (envValue == NULL) {
Yifan Hong488e16a2017-07-11 13:50:41 -070070 std::cerr << "Warning: " << key << " is missing, defaulted to " << (*value)
71 << std::endl;
72 return true;
Yifan Hong4d18bcc2017-04-07 21:47:16 +000073 }
74
75 if (!parse(envValue, value)) {
76 std::cerr << "Cannot parse " << envValue << "." << std::endl;
77 return false;
78 }
79 return true;
80 }
81
Yifan Hongdbe9db32017-12-11 19:06:11 -080082 static bool getBooleanFlag(const char* key) {
83 const char* envValue = getenv(key);
84 return envValue != nullptr && strcmp(envValue, "true") == 0;
85 }
86
87 static size_t getIntegerFlag(const char* key, size_t defaultValue = 0) {
88 std::string envValue = getenv(key);
89 if (envValue.empty()) {
90 return defaultValue;
91 }
92 size_t value;
93 if (!base::ParseUint(envValue, &value)) {
94 std::cerr << "Error: " << key << " must be a number." << std::endl;
95 return defaultValue;
96 }
97 return value;
98 }
99
Yifan Hong4650ad82017-05-01 17:28:02 -0700100 static std::string read(std::basic_istream<char>& is) {
101 std::stringstream ss;
102 ss << is.rdbuf();
103 return ss.str();
104 }
105
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700106 static bool isCommonConfig(const std::string& path) {
107 return ::android::base::Basename(path) == gBaseConfig;
108 }
109
Yifan Hongdbe9db32017-12-11 19:06:11 -0800110 static Level convertFromApiLevel(size_t apiLevel) {
111 if (apiLevel < 26) {
112 return Level::LEGACY;
113 } else if (apiLevel == 26) {
114 return Level::O;
115 } else if (apiLevel == 27) {
116 return Level::O_MR1;
117 } else {
118 return Level::UNSPECIFIED;
119 }
120 }
121
Yifan Hong079ec242017-08-25 18:53:38 -0700122 // nullptr on any error, otherwise the condition.
123 static Condition generateCondition(const std::string& path) {
124 std::string fname = ::android::base::Basename(path);
125 if (fname.size() <= gConfigPrefix.size() + gConfigSuffix.size() ||
126 !std::equal(gConfigPrefix.begin(), gConfigPrefix.end(), fname.begin()) ||
127 !std::equal(gConfigSuffix.rbegin(), gConfigSuffix.rend(), fname.rbegin())) {
128 return nullptr;
129 }
130
131 std::string sub = fname.substr(gConfigPrefix.size(),
132 fname.size() - gConfigPrefix.size() - gConfigSuffix.size());
133 if (sub.empty()) {
134 return nullptr; // should not happen
135 }
136 for (size_t i = 0; i < sub.size(); ++i) {
137 if (sub[i] == '-') {
138 sub[i] = '_';
139 continue;
140 }
141 if (isalnum(sub[i])) {
142 sub[i] = toupper(sub[i]);
143 continue;
144 }
145 std::cerr << "'" << fname << "' (in " << path
146 << ") is not a valid kernel config file name. Must match regex: "
147 << "android-base(-[0-9a-zA-Z-]+)?\\.cfg" << std::endl;
148 return nullptr;
149 }
150 sub.insert(0, "CONFIG_");
151 return std::make_unique<KernelConfig>(std::move(sub), Tristate::YES);
152 }
153
Yifan Hong79efa8a2017-07-06 14:10:28 -0700154 static bool parseFileForKernelConfigs(const std::string& path, std::vector<KernelConfig>* out) {
155 std::ifstream ifs{path};
156 if (!ifs.is_open()) {
157 std::cerr << "File '" << path << "' does not exist or cannot be read." << std::endl;
158 return false;
159 }
Yifan Hong02e94002017-07-10 15:41:56 -0700160 KernelConfigParser parser(true /* processComments */, true /* relaxedFormat */);
Yifan Hong79efa8a2017-07-06 14:10:28 -0700161 std::string content = read(ifs);
162 status_t err = parser.process(content.c_str(), content.size());
163 if (err != OK) {
Yifan Hongae53a0e2017-07-07 15:19:06 -0700164 std::cerr << parser.error();
Yifan Hong79efa8a2017-07-06 14:10:28 -0700165 return false;
166 }
167 err = parser.finish();
168 if (err != OK) {
Yifan Hongae53a0e2017-07-07 15:19:06 -0700169 std::cerr << parser.error();
Yifan Hong79efa8a2017-07-06 14:10:28 -0700170 return false;
171 }
172
173 for (auto& configPair : parser.configs()) {
174 out->push_back({});
175 KernelConfig& config = out->back();
176 config.first = std::move(configPair.first);
177 if (!parseKernelConfigTypedValue(configPair.second, &config.second)) {
178 std::cerr << "Unknown value type for key = '" << config.first << "', value = '"
179 << configPair.second << "'\n";
180 return false;
181 }
182 }
183 return true;
184 }
185
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700186 static bool parseFilesForKernelConfigs(const std::string& path,
187 std::vector<ConditionedConfig>* out) {
188 out->clear();
189 ConditionedConfig commonConfig;
190 bool foundCommonConfig = false;
Steve Muckle0bef8682017-07-31 15:47:15 -0700191 bool ret = true;
192 char *pathIter;
193 char *modPath = new char[path.length() + 1];
194 strcpy(modPath, path.c_str());
195 pathIter = strtok(modPath, ":");
196 while (ret && pathIter != NULL) {
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700197 if (isCommonConfig(pathIter)) {
198 ret &= parseFileForKernelConfigs(pathIter, &commonConfig.second);
199 foundCommonConfig = true;
200 } else {
Yifan Hong079ec242017-08-25 18:53:38 -0700201 Condition condition = generateCondition(pathIter);
202 ret &= (condition != nullptr);
203
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700204 std::vector<KernelConfig> kernelConfigs;
205 if ((ret &= parseFileForKernelConfigs(pathIter, &kernelConfigs)))
Yifan Hong079ec242017-08-25 18:53:38 -0700206 out->emplace_back(std::move(condition), std::move(kernelConfigs));
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700207 }
Steve Muckle0bef8682017-07-31 15:47:15 -0700208 pathIter = strtok(NULL, ":");
209 }
Luis A. Lozano82266ae2017-08-22 16:30:11 -0700210 delete[] modPath;
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700211
212 if (!foundCommonConfig) {
213 std::cerr << "No android-base.cfg is found in these paths: '" << path << "'"
214 << std::endl;
215 }
216 ret &= foundCommonConfig;
217 // first element is always common configs (no conditions).
218 out->insert(out->begin(), std::move(commonConfig));
Steve Muckle0bef8682017-07-31 15:47:15 -0700219 return ret;
220 }
221
Yifan Hongdbe9db32017-12-11 19:06:11 -0800222 static std::string getFileNameFromPath(std::string path) {
223 auto idx = path.find_last_of("\\/");
224 if (idx != std::string::npos) {
225 path.erase(0, idx + 1);
226 }
227 return path;
228 }
229
Yifan Hong9aa63702017-05-16 16:37:50 -0700230 std::basic_ostream<char>& out() const {
231 return mOutFileRef == nullptr ? std::cout : *mOutFileRef;
232 }
233
Yifan Hongdbe9db32017-12-11 19:06:11 -0800234 template <typename S>
235 using Schemas = std::vector<std::pair<std::string, S>>;
236 using HalManifests = Schemas<HalManifest>;
237 using CompatibilityMatrices = Schemas<CompatibilityMatrix>;
238
239 bool assembleHalManifest(HalManifests* halManifests) {
Yifan Hong4650ad82017-05-01 17:28:02 -0700240 std::string error;
Yifan Hongdbe9db32017-12-11 19:06:11 -0800241 HalManifest* halManifest = &halManifests->front().second;
242 for (auto it = halManifests->begin() + 1; it != halManifests->end(); ++it) {
243 const std::string& path = it->first;
244 HalManifest& halToAdd = it->second;
245
246 if (halToAdd.level() != Level::UNSPECIFIED) {
247 if (halManifest->level() == Level::UNSPECIFIED) {
248 halManifest->mLevel = halToAdd.level();
249 } else if (halManifest->level() != halToAdd.level()) {
250 std::cerr << "Inconsistent FCM Version in HAL manifests:" << std::endl
251 << " File '" << halManifests->front().first << "' has level "
252 << halManifest->level() << std::endl
253 << " File '" << path << "' has level " << halToAdd.level()
254 << std::endl;
255 return false;
256 }
257 }
258
Yifan Hongea25dd42017-12-18 17:03:24 -0800259 if (!halManifest->addAllHals(&halToAdd, &error)) {
Yifan Hongdbe9db32017-12-11 19:06:11 -0800260 std::cerr << "File \"" << path << "\" cannot be added: conflict on HAL \"" << error
261 << "\" with an existing HAL. See <hal> with the same name "
262 << "in previously parsed files or previously declared in this file."
263 << std::endl;
264 return false;
265 }
266 }
Yifan Hong9aa63702017-05-16 16:37:50 -0700267
268 if (halManifest->mType == SchemaType::DEVICE) {
269 if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) {
270 return false;
271 }
Yifan Hongdbe9db32017-12-11 19:06:11 -0800272 if (!setDeviceFcmVersion(halManifest)) {
273 return false;
274 }
Yifan Hong9aa63702017-05-16 16:37:50 -0700275 }
276
277 if (mOutputMatrix) {
278 CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix();
279 if (!halManifest->checkCompatibility(generatedMatrix, &error)) {
280 std::cerr << "FATAL ERROR: cannot generate a compatible matrix: " << error
281 << std::endl;
282 }
283 out() << "<!-- \n"
284 " Autogenerated skeleton compatibility matrix. \n"
285 " Use with caution. Modify it to suit your needs.\n"
286 " All HALs are set to optional.\n"
287 " Many entries other than HALs are zero-filled and\n"
288 " require human attention. \n"
289 "-->\n"
Yifan Honga2635c42017-12-12 13:20:33 -0800290 << gCompatibilityMatrixConverter(generatedMatrix, mSerializeFlags);
Yifan Hong9aa63702017-05-16 16:37:50 -0700291 } else {
Yifan Honga2635c42017-12-12 13:20:33 -0800292 out() << gHalManifestConverter(*halManifest, mSerializeFlags);
Yifan Hong9aa63702017-05-16 16:37:50 -0700293 }
294 out().flush();
295
296 if (mCheckFile.is_open()) {
297 CompatibilityMatrix checkMatrix;
298 if (!gCompatibilityMatrixConverter(&checkMatrix, read(mCheckFile))) {
299 std::cerr << "Cannot parse check file as a compatibility matrix: "
300 << gCompatibilityMatrixConverter.lastError() << std::endl;
301 return false;
302 }
303 if (!halManifest->checkCompatibility(checkMatrix, &error)) {
304 std::cerr << "Not compatible: " << error << std::endl;
305 return false;
306 }
307 }
308
309 return true;
310 }
311
Yifan Honge88e1672017-08-24 14:42:54 -0700312 bool assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix* matrix) {
Yifan Hong4c34fee2017-08-24 16:03:34 -0700313 if (!matrix->framework.mKernels.empty()) {
314 // Remove hard-coded <kernel version="x.y.z" /> in legacy files.
315 std::cerr << "WARNING: framework compatibility matrix has hard-coded kernel"
316 << " requirements for version";
317 for (const auto& kernel : matrix->framework.mKernels) {
318 std::cerr << " " << kernel.minLts();
319 }
320 std::cerr << ". Hard-coded requirements are removed." << std::endl;
321 matrix->framework.mKernels.clear();
322 }
Yifan Honge88e1672017-08-24 14:42:54 -0700323 for (const auto& pair : mKernels) {
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700324 std::vector<ConditionedConfig> conditionedConfigs;
325 if (!parseFilesForKernelConfigs(pair.second, &conditionedConfigs)) {
Yifan Honge88e1672017-08-24 14:42:54 -0700326 return false;
327 }
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700328 for (ConditionedConfig& conditionedConfig : conditionedConfigs) {
Yifan Hong48602df2017-08-28 13:04:12 -0700329 MatrixKernel kernel(KernelVersion{pair.first.majorVer, pair.first.minorVer, 0u},
330 std::move(conditionedConfig.second));
Yifan Hong079ec242017-08-25 18:53:38 -0700331 if (conditionedConfig.first != nullptr)
332 kernel.mConditions.push_back(std::move(*conditionedConfig.first));
333 matrix->framework.mKernels.push_back(std::move(kernel));
Yifan Hong9a8b1a72017-08-25 17:55:33 -0700334 }
Yifan Honge88e1672017-08-24 14:42:54 -0700335 }
336 return true;
337 }
338
Yifan Hongdbe9db32017-12-11 19:06:11 -0800339 bool setDeviceFcmVersion(HalManifest* manifest) {
340 size_t shippingApiLevel = getIntegerFlag("PRODUCT_SHIPPING_API_LEVEL");
Yifan Hong9aa63702017-05-16 16:37:50 -0700341
Yifan Hongdbe9db32017-12-11 19:06:11 -0800342 if (manifest->level() != Level::UNSPECIFIED) {
343 return true;
344 }
345 if (!getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) {
346 manifest->mLevel = Level::LEGACY;
347 return true;
348 }
349 // TODO(b/70628538): Do not infer from Shipping API level.
350 if (shippingApiLevel) {
351 std::cerr << "Warning: Shipping FCM Version is inferred from Shipping API level. "
352 << "Declare Shipping FCM Version in device manifest directly." << std::endl;
353 manifest->mLevel = convertFromApiLevel(shippingApiLevel);
354 if (manifest->mLevel == Level::UNSPECIFIED) {
355 std::cerr << "Error: Shipping FCM Version cannot be inferred from Shipping API "
356 << "level " << shippingApiLevel << "."
357 << "Declare Shipping FCM Version in device manifest directly."
358 << std::endl;
359 return false;
360 }
361 return true;
362 }
363 // TODO(b/69638851): should be an error if Shipping API level is not defined.
364 // For now, just leave it empty; when framework compatibility matrix is built,
365 // lowest FCM Version is assumed.
366 std::cerr << "Warning: Shipping FCM Version cannot be inferred, because:" << std::endl
367 << " (1) It is not explicitly declared in device manifest;" << std::endl
368 << " (2) PRODUCT_ENFORCE_VINTF_MANIFEST is set to true;" << std::endl
369 << " (3) PRODUCT_SHIPPING_API_LEVEL is undefined." << std::endl
370 << "Assuming 'unspecified' Shipping FCM Version. " << std::endl
371 << "To remove this warning, define 'level' attribute in device manifest."
372 << std::endl;
373 return true;
374 }
375
376 Level getLowestFcmVersion(const CompatibilityMatrices& matrices) {
377 Level ret = Level::UNSPECIFIED;
378 for (const auto& e : matrices) {
379 if (ret == Level::UNSPECIFIED || ret > e.second.level()) {
380 ret = e.second.level();
381 }
382 }
383 return ret;
384 }
385
386 bool assembleCompatibilityMatrix(CompatibilityMatrices* matrices) {
387 std::string error;
388 CompatibilityMatrix* matrix = nullptr;
Yifan Hong9aa63702017-05-16 16:37:50 -0700389 KernelSepolicyVersion kernelSepolicyVers;
390 Version sepolicyVers;
Yifan Hongdbe9db32017-12-11 19:06:11 -0800391 std::unique_ptr<HalManifest> checkManifest;
392 if (matrices->front().second.mType == SchemaType::DEVICE) {
393 matrix = &matrices->front().second;
394 }
395
396 if (matrices->front().second.mType == SchemaType::FRAMEWORK) {
397 Level deviceLevel = Level::UNSPECIFIED;
398 std::vector<std::string> fileList;
399 if (mCheckFile.is_open()) {
400 checkManifest = std::make_unique<HalManifest>();
401 if (!gHalManifestConverter(checkManifest.get(), read(mCheckFile))) {
402 std::cerr << "Cannot parse check file as a HAL manifest: "
403 << gHalManifestConverter.lastError() << std::endl;
404 return false;
405 }
406 deviceLevel = checkManifest->level();
407 }
408
409 if (deviceLevel == Level::UNSPECIFIED) {
410 // For GSI build, legacy devices that do not have a HAL manifest,
411 // and devices in development, merge all compatibility matrices.
412 deviceLevel = getLowestFcmVersion(*matrices);
413 }
414
415 for (auto& e : *matrices) {
416 if (e.second.level() == deviceLevel) {
417 fileList.push_back(e.first);
418 matrix = &e.second;
419 }
420 }
421 if (matrix == nullptr) {
422 std::cerr << "FATAL ERROR: cannot find matrix with level '" << deviceLevel << "'"
423 << std::endl;
424 return false;
425 }
426 for (auto& e : *matrices) {
427 if (e.second.level() <= deviceLevel) {
428 continue;
429 }
430 fileList.push_back(e.first);
431 if (!matrix->addAllHalsAsOptional(&e.second, &error)) {
432 std::cerr << "File \"" << e.first << "\" cannot be added: " << error
433 << ". See <hal> with the same name "
434 << "in previously parsed files or previously declared in this file."
435 << std::endl;
436 return false;
437 }
438 }
439
Yifan Hong9aa63702017-05-16 16:37:50 -0700440 if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) {
441 return false;
442 }
443 if (!getFlag("POLICYVERS", &kernelSepolicyVers)) {
444 return false;
445 }
Yifan Honge88e1672017-08-24 14:42:54 -0700446
447 if (!assembleFrameworkCompatibilityMatrixKernels(matrix)) {
448 return false;
Yifan Hong79efa8a2017-07-06 14:10:28 -0700449 }
Yifan Honge88e1672017-08-24 14:42:54 -0700450
Yifan Hong9aa63702017-05-16 16:37:50 -0700451 matrix->framework.mSepolicy =
452 Sepolicy(kernelSepolicyVers, {{sepolicyVers.majorVer, sepolicyVers.minorVer}});
Yifan Hong7f6c00c2017-07-06 19:50:29 +0000453
454 Version avbMetaVersion;
455 if (!getFlag("FRAMEWORK_VBMETA_VERSION", &avbMetaVersion)) {
456 return false;
457 }
458 matrix->framework.mAvbMetaVersion = avbMetaVersion;
Yifan Hongdbe9db32017-12-11 19:06:11 -0800459
460 out() << "<!--" << std::endl;
461 out() << " Input:" << std::endl;
462 for (const auto& path : fileList) {
463 out() << " " << getFileNameFromPath(path) << std::endl;
464 }
465 out() << "-->" << std::endl;
Yifan Hong9aa63702017-05-16 16:37:50 -0700466 }
Yifan Honga2635c42017-12-12 13:20:33 -0800467 out() << gCompatibilityMatrixConverter(*matrix, mSerializeFlags);
Yifan Hong9aa63702017-05-16 16:37:50 -0700468 out().flush();
469
Yifan Hongdbe9db32017-12-11 19:06:11 -0800470 if (checkManifest != nullptr && getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST") &&
471 !checkManifest->checkCompatibility(*matrix, &error)) {
472 std::cerr << "Not compatible: " << error << std::endl;
473 return false;
Yifan Hong9aa63702017-05-16 16:37:50 -0700474 }
475
476 return true;
477 }
478
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700479 enum AssembleStatus { SUCCESS, FAIL_AND_EXIT, TRY_NEXT };
480 template <typename Schema, typename AssembleFunc>
481 AssembleStatus tryAssemble(const XmlConverter<Schema>& converter, const std::string& schemaName,
482 AssembleFunc assemble) {
Yifan Hongdbe9db32017-12-11 19:06:11 -0800483 Schemas<Schema> schemas;
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700484 Schema schema;
Yifan Hongaa219f52017-12-18 18:51:59 -0800485 if (!converter(&schema, read(mInFiles.front().stream()))) {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700486 return TRY_NEXT;
487 }
488 auto firstType = schema.type();
Yifan Hongaa219f52017-12-18 18:51:59 -0800489 schemas.emplace_back(mInFiles.front().name(), std::move(schema));
Yifan Hongdbe9db32017-12-11 19:06:11 -0800490
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700491 for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) {
492 Schema additionalSchema;
Yifan Hongaa219f52017-12-18 18:51:59 -0800493 const std::string& fileName = it->name();
494 if (!converter(&additionalSchema, read(it->stream()))) {
Yifan Hongdbe9db32017-12-11 19:06:11 -0800495 std::cerr << "File \"" << fileName << "\" is not a valid " << firstType << " "
496 << schemaName << " (but the first file is a valid " << firstType << " "
497 << schemaName << "). Error: " << converter.lastError() << std::endl;
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700498 return FAIL_AND_EXIT;
499 }
500 if (additionalSchema.type() != firstType) {
Yifan Hongdbe9db32017-12-11 19:06:11 -0800501 std::cerr << "File \"" << fileName << "\" is a " << additionalSchema.type() << " "
502 << schemaName << " (but a " << firstType << " " << schemaName
503 << " is expected)." << std::endl;
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700504 return FAIL_AND_EXIT;
505 }
Yifan Hongdbe9db32017-12-11 19:06:11 -0800506
507 schemas.emplace_back(fileName, std::move(additionalSchema));
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700508 }
Yifan Hongdbe9db32017-12-11 19:06:11 -0800509 return assemble(&schemas) ? SUCCESS : FAIL_AND_EXIT;
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700510 }
511
Yifan Hong9aa63702017-05-16 16:37:50 -0700512 bool assemble() {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700513 using std::placeholders::_1;
514 if (mInFiles.empty()) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700515 std::cerr << "Missing input file." << std::endl;
516 return false;
517 }
518
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700519 auto status = tryAssemble(gHalManifestConverter, "manifest",
520 std::bind(&AssembleVintf::assembleHalManifest, this, _1));
521 if (status == SUCCESS) return true;
522 if (status == FAIL_AND_EXIT) return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000523
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700524 resetInFiles();
Yifan Honga59d2562017-04-18 18:01:16 -0700525
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700526 status = tryAssemble(gCompatibilityMatrixConverter, "compatibility matrix",
527 std::bind(&AssembleVintf::assembleCompatibilityMatrix, this, _1));
528 if (status == SUCCESS) return true;
529 if (status == FAIL_AND_EXIT) return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000530
Yifan Hong959ee1b2017-04-28 14:37:56 -0700531 std::cerr << "Input file has unknown format." << std::endl
532 << "Error when attempting to convert to manifest: "
533 << gHalManifestConverter.lastError() << std::endl
534 << "Error when attempting to convert to compatibility matrix: "
535 << gCompatibilityMatrixConverter.lastError() << std::endl;
536 return false;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000537 }
Yifan Hong9aa63702017-05-16 16:37:50 -0700538
539 bool openOutFile(const char* path) {
540 mOutFileRef = std::make_unique<std::ofstream>();
541 mOutFileRef->open(path);
542 return mOutFileRef->is_open();
543 }
544
545 bool openInFile(const char* path) {
Yifan Hongaa219f52017-12-18 18:51:59 -0800546 auto s = std::make_unique<std::ifstream>(path);
547 if (!s->is_open()) return false;
548 mInFiles.emplace(mInFiles.end(), std::string{path}, std::move(s));
549 return true;
Yifan Hong9aa63702017-05-16 16:37:50 -0700550 }
551
552 bool openCheckFile(const char* path) {
553 mCheckFile.open(path);
554 return mCheckFile.is_open();
555 }
556
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700557 void resetInFiles() {
558 for (auto& inFile : mInFiles) {
Yifan Hongaa219f52017-12-18 18:51:59 -0800559 inFile.stream().clear();
560 inFile.stream().seekg(0);
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700561 }
562 }
563
Yifan Hong9aa63702017-05-16 16:37:50 -0700564 void setOutputMatrix() { mOutputMatrix = true; }
565
Yifan Honga2635c42017-12-12 13:20:33 -0800566 bool setHalsOnly() {
567 if (mSerializeFlags) return false;
568 mSerializeFlags |= SerializeFlag::HALS_ONLY;
569 return true;
570 }
571
572 bool setNoHals() {
573 if (mSerializeFlags) return false;
574 mSerializeFlags |= SerializeFlag::NO_HALS;
575 return true;
576 }
577
Yifan Hong79efa8a2017-07-06 14:10:28 -0700578 bool addKernel(const std::string& kernelArg) {
579 auto ind = kernelArg.find(':');
580 if (ind == std::string::npos) {
581 std::cerr << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl;
582 return false;
583 }
584 std::string kernelVerStr{kernelArg.begin(), kernelArg.begin() + ind};
585 std::string kernelConfigPath{kernelArg.begin() + ind + 1, kernelArg.end()};
586 Version kernelVer;
587 if (!parse(kernelVerStr, &kernelVer)) {
588 std::cerr << "Unrecognized kernel version '" << kernelVerStr << "'" << std::endl;
589 return false;
590 }
Yifan Hong48602df2017-08-28 13:04:12 -0700591 if (mKernels.find(kernelVer) != mKernels.end()) {
592 std::cerr << "Multiple --kernel for " << kernelVer << " is specified." << std::endl;
593 return false;
594 }
595 mKernels[kernelVer] = kernelConfigPath;
Yifan Hong79efa8a2017-07-06 14:10:28 -0700596 return true;
597 }
598
Yifan Hong9aa63702017-05-16 16:37:50 -0700599 private:
Yifan Hongaa219f52017-12-18 18:51:59 -0800600 std::vector<NamedIstream> mInFiles;
Yifan Hong9aa63702017-05-16 16:37:50 -0700601 std::unique_ptr<std::ofstream> mOutFileRef;
602 std::ifstream mCheckFile;
603 bool mOutputMatrix = false;
Yifan Honga2635c42017-12-12 13:20:33 -0800604 SerializeFlags mSerializeFlags = SerializeFlag::EVERYTHING;
Yifan Hong48602df2017-08-28 13:04:12 -0700605 std::map<Version, std::string> mKernels;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000606};
607
608} // namespace vintf
609} // namespace android
610
611void help() {
Yifan Hong9aa63702017-05-16 16:37:50 -0700612 std::cerr << "assemble_vintf: Checks if a given manifest / matrix file is valid and \n"
613 " fill in build-time flags into the given file.\n"
614 "assemble_vintf -h\n"
615 " Display this help text.\n"
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700616 "assemble_vintf -i <input file>[:<input file>[...]] [-o <output file>] [-m]\n"
617 " [-c [<check file>]]\n"
Yifan Hong9aa63702017-05-16 16:37:50 -0700618 " Fill in build-time flags into the given file.\n"
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700619 " -i <input file>[:<input file>[...]]\n"
620 " A list of input files. Format is automatically detected for the\n"
621 " first file, and the remaining files must have the same format.\n"
622 " Files other than the first file should only have <hal> defined;\n"
623 " other entries are ignored.\n"
Yifan Hong9aa63702017-05-16 16:37:50 -0700624 " -o <output file>\n"
625 " Optional output file. If not specified, write to stdout.\n"
626 " -m\n"
627 " a compatible compatibility matrix is\n"
628 " generated instead; for example, given a device manifest,\n"
629 " a framework compatibility matrix is generated. This flag\n"
630 " is ignored when input is a compatibility matrix.\n"
631 " -c [<check file>]\n"
632 " After writing the output file, check compatibility between\n"
633 " output file and check file.\n"
634 " If -c is set but the check file is not specified, a warning\n"
635 " message is written to stderr. Return 0.\n"
636 " If the check file is specified but is not compatible, an error\n"
Yifan Hong79efa8a2017-07-06 14:10:28 -0700637 " message is written to stderr. Return 1.\n"
Steve Muckle0bef8682017-07-31 15:47:15 -0700638 " --kernel=<version>:<android-base.cfg>[:<android-base-arch.cfg>[...]]\n"
Yifan Hong79efa8a2017-07-06 14:10:28 -0700639 " Add a kernel entry to framework compatibility matrix.\n"
640 " Ignored for other input format.\n"
641 " <version> has format: 3.18\n"
Steve Muckle0bef8682017-07-31 15:47:15 -0700642 " <android-base.cfg> is the location of android-base.cfg\n"
643 " <android-base-arch.cfg> is the location of an optional\n"
Yifan Honga2635c42017-12-12 13:20:33 -0800644 " arch-specific config fragment, more than one may be specified\n"
645 " -l, --hals-only\n"
646 " Output has only <hal> entries. Cannot be used with -n.\n"
647 " -n, --no-hals\n"
648 " Output has no <hal> entries (but all other entries).\n"
649 " Cannot be used with -l.\n";
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000650}
651
652int main(int argc, char **argv) {
Yifan Honga2635c42017-12-12 13:20:33 -0800653 const struct option longopts[] = {{"kernel", required_argument, NULL, 'k'},
654 {"hals-only", no_argument, NULL, 'l'},
655 {"no-hals", no_argument, NULL, 'n'},
656 {0, 0, 0, 0}};
Yifan Hong9aa63702017-05-16 16:37:50 -0700657
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700658 std::string outFilePath;
Yifan Hong9aa63702017-05-16 16:37:50 -0700659 ::android::vintf::AssembleVintf assembleVintf;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000660 int res;
Yifan Hong9aa63702017-05-16 16:37:50 -0700661 int optind;
Yifan Honga2635c42017-12-12 13:20:33 -0800662 while ((res = getopt_long(argc, argv, "hi:o:mc:nl", longopts, &optind)) >= 0) {
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000663 switch (res) {
664 case 'i': {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700665 char* inFilePath = strtok(optarg, ":");
666 while (inFilePath != NULL) {
667 if (!assembleVintf.openInFile(inFilePath)) {
668 std::cerr << "Failed to open " << optarg << std::endl;
669 return 1;
670 }
671 inFilePath = strtok(NULL, ":");
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000672 }
673 } break;
674
675 case 'o': {
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700676 outFilePath = optarg;
Yifan Hong9aa63702017-05-16 16:37:50 -0700677 if (!assembleVintf.openOutFile(optarg)) {
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000678 std::cerr << "Failed to open " << optarg << std::endl;
679 return 1;
680 }
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000681 } break;
682
Yifan Honga59d2562017-04-18 18:01:16 -0700683 case 'm': {
Yifan Hong9aa63702017-05-16 16:37:50 -0700684 assembleVintf.setOutputMatrix();
Yifan Honga59d2562017-04-18 18:01:16 -0700685 } break;
686
Yifan Hong4650ad82017-05-01 17:28:02 -0700687 case 'c': {
688 if (strlen(optarg) != 0) {
Yifan Hong9aa63702017-05-16 16:37:50 -0700689 if (!assembleVintf.openCheckFile(optarg)) {
Yifan Hong4650ad82017-05-01 17:28:02 -0700690 std::cerr << "Failed to open " << optarg << std::endl;
691 return 1;
692 }
693 } else {
694 std::cerr << "WARNING: no compatibility check is done on "
Yifan Hongbfb3c1d2017-05-24 14:38:48 -0700695 << (outFilePath.empty() ? "output" : outFilePath) << std::endl;
Yifan Hong4650ad82017-05-01 17:28:02 -0700696 }
697 } break;
698
Yifan Hong79efa8a2017-07-06 14:10:28 -0700699 case 'k': {
700 if (!assembleVintf.addKernel(optarg)) {
701 std::cerr << "ERROR: Unrecognized --kernel argument." << std::endl;
702 return 1;
703 }
704 } break;
705
Yifan Honga2635c42017-12-12 13:20:33 -0800706 case 'l': {
707 if (!assembleVintf.setHalsOnly()) {
708 return 1;
709 }
710 } break;
711
712 case 'n': {
713 if (!assembleVintf.setNoHals()) {
714 return 1;
715 }
716 } break;
717
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000718 case 'h':
719 default: {
720 help();
721 return 1;
722 } break;
723 }
724 }
725
Yifan Hong9aa63702017-05-16 16:37:50 -0700726 bool success = assembleVintf.assemble();
Yifan Hong4650ad82017-05-01 17:28:02 -0700727
728 return success ? 0 : 1;
Yifan Hong4d18bcc2017-04-07 21:47:16 +0000729}