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 <stdlib.h> 18 #include <unistd.h> 19 20 #include <fstream> 21 #include <iostream> 22 #include <sstream> 23 #include <string> 24 #include <unordered_map> 25 26 #include <android-base/file.h> 27 #include <android-base/parseint.h> 28 #include <android-base/strings.h> 29 #include <libvts_vintf_test_common/common.h> 30 #include <vintf/AssembleVintf.h> 31 #include <vintf/KernelConfigParser.h> 32 #include <vintf/parse_string.h> 33 #include <vintf/parse_xml.h> 34 #include "utils.h" 35 36 #define BUFFER_SIZE sysconf(_SC_PAGESIZE) 37 38 namespace android { 39 namespace vintf { 40 41 static const std::string gConfigPrefix = "android-base-"; 42 static const std::string gConfigSuffix = ".config"; 43 static const std::string gBaseConfig = "android-base.config"; 44 45 // An input stream with a name. 46 // The input stream may be an actual file, or a stringstream for testing. 47 // It takes ownership on the istream. 48 class NamedIstream { 49 public: 50 NamedIstream() = default; 51 NamedIstream(const std::string& name, std::unique_ptr<std::istream>&& stream) 52 : mName(name), mStream(std::move(stream)) {} 53 const std::string& name() const { return mName; } 54 std::istream& stream() { return *mStream; } 55 bool hasStream() { return mStream != nullptr; } 56 57 private: 58 std::string mName; 59 std::unique_ptr<std::istream> mStream; 60 }; 61 62 /** 63 * Slurps the device manifest file and add build time flag to it. 64 */ 65 class AssembleVintfImpl : public AssembleVintf { 66 using Condition = std::unique_ptr<KernelConfig>; 67 using ConditionedConfig = std::pair<Condition, std::vector<KernelConfig> /* configs */>; 68 69 public: 70 void setFakeEnv(const std::string& key, const std::string& value) { mFakeEnv[key] = value; } 71 72 std::string getEnv(const std::string& key) const { 73 auto it = mFakeEnv.find(key); 74 if (it != mFakeEnv.end()) { 75 return it->second; 76 } 77 const char* envValue = getenv(key.c_str()); 78 return envValue != nullptr ? std::string(envValue) : std::string(); 79 } 80 81 // Get environment variable and split with space. 82 std::vector<std::string> getEnvList(const std::string& key) const { 83 std::vector<std::string> ret; 84 for (auto&& v : base::Split(getEnv(key), " ")) { 85 v = base::Trim(v); 86 if (!v.empty()) { 87 ret.push_back(v); 88 } 89 } 90 return ret; 91 } 92 93 template <typename T> 94 bool getFlag(const std::string& key, T* value, bool log = true) const { 95 std::string envValue = getEnv(key); 96 if (envValue.empty()) { 97 if (log) { 98 std::cerr << "Warning: " << key << " is missing, defaulted to " << (*value) << "." 99 << std::endl; 100 } 101 return true; 102 } 103 104 if (!parse(envValue, value)) { 105 std::cerr << "Cannot parse " << envValue << "." << std::endl; 106 return false; 107 } 108 return true; 109 } 110 111 /** 112 * Set *out to environment variable only if *out is default constructed. 113 * Return false if a fatal error has occurred: 114 * - The environment variable has an unknown format 115 * - The value of the environment variable does not match a predefined variable in the files 116 */ 117 template <typename T> 118 bool getFlagIfUnset(const std::string& envKey, T* out) const { 119 bool hasExistingValue = !(*out == T{}); 120 121 bool hasEnvValue = false; 122 T envValue; 123 std::string envStrValue = getEnv(envKey); 124 if (!envStrValue.empty()) { 125 if (!parse(envStrValue, &envValue)) { 126 std::cerr << "Cannot parse " << envValue << "." << std::endl; 127 return false; 128 } 129 hasEnvValue = true; 130 } 131 132 if (hasExistingValue) { 133 if (hasEnvValue && (*out != envValue)) { 134 std::cerr << "Cannot override existing value " << *out << " with " << envKey 135 << " (which is " << envValue << ")." << std::endl; 136 return false; 137 } 138 return true; 139 } 140 if (hasEnvValue) { 141 *out = envValue; 142 } 143 return true; 144 } 145 146 bool getBooleanFlag(const std::string& key) const { return getEnv(key) == std::string("true"); } 147 148 size_t getIntegerFlag(const std::string& key, size_t defaultValue = 0) const { 149 std::string envValue = getEnv(key); 150 if (envValue.empty()) { 151 return defaultValue; 152 } 153 size_t value; 154 if (!base::ParseUint(envValue, &value)) { 155 std::cerr << "Error: " << key << " must be a number." << std::endl; 156 return defaultValue; 157 } 158 return value; 159 } 160 161 static std::string read(std::basic_istream<char>& is) { 162 std::stringstream ss; 163 ss << is.rdbuf(); 164 return ss.str(); 165 } 166 167 // Return true if name of file is "android-base.config". This file must be specified 168 // exactly once for each kernel version. These requirements do not have any conditions. 169 static bool isCommonConfig(const std::string& path) { 170 return ::android::base::Basename(path) == gBaseConfig; 171 } 172 173 // Return true if name of file matches "android-base-foo.config". 174 // Zero or more conditional configs may be specified for each kernel version. These 175 // requirements are conditional on CONFIG_FOO=y. 176 static bool isConditionalConfig(const std::string& path) { 177 auto fname = ::android::base::Basename(path); 178 return ::android::base::StartsWith(fname, gConfigPrefix) && 179 ::android::base::EndsWith(fname, gConfigSuffix); 180 } 181 182 // Return true for all other file names (i.e. not android-base.config, and not conditional 183 // configs.) 184 // Zero or more conditional configs may be specified for each kernel version. 185 // These requirements do not have any conditions. 186 static bool isExtraCommonConfig(const std::string& path) { 187 return !isCommonConfig(path) && !isConditionalConfig(path); 188 } 189 190 // nullptr on any error, otherwise the condition. 191 static Condition generateCondition(const std::string& path) { 192 if (!isConditionalConfig(path)) { 193 return nullptr; 194 } 195 auto fname = ::android::base::Basename(path); 196 std::string sub = fname.substr(gConfigPrefix.size(), 197 fname.size() - gConfigPrefix.size() - gConfigSuffix.size()); 198 if (sub.empty()) { 199 return nullptr; // should not happen 200 } 201 for (size_t i = 0; i < sub.size(); ++i) { 202 if (sub[i] == '-') { 203 sub[i] = '_'; 204 continue; 205 } 206 if (isalnum(sub[i])) { 207 sub[i] = toupper(sub[i]); 208 continue; 209 } 210 std::cerr << "'" << fname << "' (in " << path 211 << ") is not a valid kernel config file name. Must match regex: " 212 << "android-base(-[0-9a-zA-Z-]+)?\\" << gConfigSuffix 213 << std::endl; 214 return nullptr; 215 } 216 sub.insert(0, "CONFIG_"); 217 return std::make_unique<KernelConfig>(std::move(sub), Tristate::YES); 218 } 219 220 static bool parseFileForKernelConfigs(std::basic_istream<char>& stream, 221 std::vector<KernelConfig>* out) { 222 KernelConfigParser parser(true /* processComments */, true /* relaxedFormat */); 223 status_t err = parser.processAndFinish(read(stream)); 224 if (err != OK) { 225 std::cerr << parser.error(); 226 return false; 227 } 228 229 for (auto& configPair : parser.configs()) { 230 out->push_back({}); 231 KernelConfig& config = out->back(); 232 config.first = std::move(configPair.first); 233 if (!parseKernelConfigTypedValue(configPair.second, &config.second)) { 234 std::cerr << "Unknown value type for key = '" << config.first << "', value = '" 235 << configPair.second << "'\n"; 236 return false; 237 } 238 } 239 return true; 240 } 241 242 static bool parseFilesForKernelConfigs(std::vector<NamedIstream>* streams, 243 std::vector<ConditionedConfig>* out) { 244 out->clear(); 245 ConditionedConfig commonConfig; 246 bool foundCommonConfig = false; 247 bool ret = true; 248 249 for (auto& namedStream : *streams) { 250 if (isCommonConfig(namedStream.name()) || isExtraCommonConfig(namedStream.name())) { 251 if (!parseFileForKernelConfigs(namedStream.stream(), &commonConfig.second)) { 252 std::cerr << "Failed to generate common configs for file " 253 << namedStream.name(); 254 ret = false; 255 } 256 if (isCommonConfig(namedStream.name())) { 257 foundCommonConfig = true; 258 } 259 } else { 260 Condition condition = generateCondition(namedStream.name()); 261 if (condition == nullptr) { 262 std::cerr << "Failed to generate conditional configs for file " 263 << namedStream.name(); 264 ret = false; 265 } 266 267 std::vector<KernelConfig> kernelConfigs; 268 if ((ret &= parseFileForKernelConfigs(namedStream.stream(), &kernelConfigs))) 269 out->emplace_back(std::move(condition), std::move(kernelConfigs)); 270 } 271 } 272 273 if (!foundCommonConfig) { 274 std::cerr << "No " << gBaseConfig << " is found in these paths:" << std::endl; 275 for (auto& namedStream : *streams) { 276 std::cerr << " " << namedStream.name() << std::endl; 277 } 278 ret = false; 279 } 280 // first element is always common configs (no conditions). 281 out->insert(out->begin(), std::move(commonConfig)); 282 return ret; 283 } 284 285 std::basic_ostream<char>& out() const { return mOutRef == nullptr ? std::cout : *mOutRef; } 286 287 // If -c is provided, check it. 288 bool checkDualFile(const HalManifest& manifest, const CompatibilityMatrix& matrix) { 289 if (getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) { 290 std::string error; 291 if (!manifest.checkCompatibility(matrix, &error, mCheckFlags)) { 292 std::cerr << "Not compatible: " << error << std::endl; 293 return false; 294 } 295 } 296 return true; 297 } 298 299 using HalManifests = std::vector<HalManifest>; 300 using CompatibilityMatrices = std::vector<CompatibilityMatrix>; 301 302 template <typename M> 303 void outputInputs(const std::vector<M>& inputs) { 304 out() << "<!--" << std::endl; 305 out() << " Input:" << std::endl; 306 for (const auto& e : inputs) { 307 if (!e.fileName().empty()) { 308 out() << " " << base::Basename(e.fileName()) << std::endl; 309 } 310 } 311 out() << "-->" << std::endl; 312 } 313 314 // Parse --kernel arguments and write to output manifest. 315 bool setDeviceManifestKernel(HalManifest* manifest) { 316 if (mKernels.empty()) { 317 return true; 318 } 319 if (mKernels.size() > 1) { 320 std::cerr << "Warning: multiple --kernel is specified when building device manifest. " 321 << "Only the first one will be used." << std::endl; 322 } 323 auto& kernelArg = *mKernels.begin(); 324 const auto& kernelVer = kernelArg.first; 325 auto& kernelConfigFiles = kernelArg.second; 326 // addKernel() guarantees that !kernelConfigFiles.empty(). 327 if (kernelConfigFiles.size() > 1) { 328 std::cerr << "Warning: multiple config files are specified in --kernel when building " 329 << "device manfiest. Only the first one will be used." << std::endl; 330 } 331 332 KernelConfigParser parser(true /* processComments */, false /* relaxedFormat */); 333 status_t err = parser.processAndFinish(read(kernelConfigFiles[0].stream())); 334 if (err != OK) { 335 std::cerr << parser.error(); 336 return false; 337 } 338 339 // Set version and configs in manifest. 340 auto kernel_info = std::make_optional<KernelInfo>(); 341 kernel_info->mVersion = kernelVer; 342 kernel_info->mConfigs = parser.configs(); 343 std::string error; 344 if (!manifest->mergeKernel(&kernel_info, &error)) { 345 std::cerr << error << "\n"; 346 return false; 347 } 348 349 return true; 350 } 351 352 bool assembleHalManifest(HalManifests* halManifests) { 353 std::string error; 354 HalManifest* halManifest = &halManifests->front(); 355 for (auto it = halManifests->begin() + 1; it != halManifests->end(); ++it) { 356 const std::string& path = it->fileName(); 357 HalManifest& manifestToAdd = *it; 358 359 if (manifestToAdd.level() != Level::UNSPECIFIED) { 360 if (halManifest->level() == Level::UNSPECIFIED) { 361 halManifest->mLevel = manifestToAdd.level(); 362 } else if (halManifest->level() != manifestToAdd.level()) { 363 std::cerr << "Inconsistent FCM Version in HAL manifests:" << std::endl 364 << " File '" << halManifests->front().fileName() << "' has level " 365 << halManifest->level() << std::endl 366 << " File '" << path << "' has level " << manifestToAdd.level() 367 << std::endl; 368 return false; 369 } 370 } 371 372 if (!halManifest->addAll(&manifestToAdd, &error)) { 373 std::cerr << "File \"" << path << "\" cannot be added: " << error << std::endl; 374 return false; 375 } 376 } 377 378 if (halManifest->mType == SchemaType::DEVICE) { 379 if (!getFlagIfUnset("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) { 380 return false; 381 } 382 383 if (!setDeviceFcmVersion(halManifest)) { 384 return false; 385 } 386 387 if (!setDeviceManifestKernel(halManifest)) { 388 return false; 389 } 390 } 391 392 if (halManifest->mType == SchemaType::FRAMEWORK) { 393 for (auto&& v : getEnvList("PROVIDED_VNDK_VERSIONS")) { 394 halManifest->framework.mVendorNdks.emplace_back(std::move(v)); 395 } 396 397 for (auto&& v : getEnvList("PLATFORM_SYSTEMSDK_VERSIONS")) { 398 halManifest->framework.mSystemSdk.mVersions.emplace(std::move(v)); 399 } 400 } 401 402 outputInputs(*halManifests); 403 404 if (mOutputMatrix) { 405 CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix(); 406 if (!halManifest->checkCompatibility(generatedMatrix, &error, mCheckFlags)) { 407 std::cerr << "FATAL ERROR: cannot generate a compatible matrix: " << error 408 << std::endl; 409 } 410 out() << "<!-- \n" 411 " Autogenerated skeleton compatibility matrix. \n" 412 " Use with caution. Modify it to suit your needs.\n" 413 " All HALs are set to optional.\n" 414 " Many entries other than HALs are zero-filled and\n" 415 " require human attention. \n" 416 "-->\n" 417 << toXml(generatedMatrix, mSerializeFlags); 418 } else { 419 out() << toXml(*halManifest, mSerializeFlags); 420 } 421 out().flush(); 422 423 if (mCheckFile.hasStream()) { 424 CompatibilityMatrix checkMatrix; 425 checkMatrix.setFileName(mCheckFile.name()); 426 if (!fromXml(&checkMatrix, read(mCheckFile.stream()), &error)) { 427 std::cerr << "Cannot parse check file as a compatibility matrix: " << error 428 << std::endl; 429 return false; 430 } 431 if (!checkDualFile(*halManifest, checkMatrix)) { 432 return false; 433 } 434 } 435 436 return true; 437 } 438 439 // Parse --kernel arguments and write to output matrix. 440 bool assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix* matrix) { 441 for (auto& pair : mKernels) { 442 std::vector<ConditionedConfig> conditionedConfigs; 443 if (!parseFilesForKernelConfigs(&pair.second, &conditionedConfigs)) { 444 return false; 445 } 446 for (ConditionedConfig& conditionedConfig : conditionedConfigs) { 447 MatrixKernel kernel(KernelVersion{pair.first}, std::move(conditionedConfig.second)); 448 if (conditionedConfig.first != nullptr) 449 kernel.mConditions.push_back(std::move(*conditionedConfig.first)); 450 std::string error; 451 if (!matrix->addKernel(std::move(kernel), &error)) { 452 std::cerr << "Error:" << error << std::endl; 453 return false; 454 }; 455 } 456 } 457 return true; 458 } 459 460 bool setDeviceFcmVersion(HalManifest* manifest) { 461 // Not needed for generating empty manifest for DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE. 462 if (getBooleanFlag("VINTF_IGNORE_TARGET_FCM_VERSION")) { 463 return true; 464 } 465 466 size_t shippingApiLevel = getIntegerFlag("PRODUCT_SHIPPING_API_LEVEL"); 467 468 if (manifest->level() != Level::UNSPECIFIED) { 469 if (shippingApiLevel != 0) { 470 auto res = android::vintf::testing::TestTargetFcmVersion(manifest->level(), 471 shippingApiLevel); 472 if (!res.ok()) std::cerr << "Warning: " << res.error() << std::endl; 473 } 474 return true; 475 } 476 if (!getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) { 477 manifest->mLevel = Level::LEGACY; 478 return true; 479 } 480 // TODO(b/70628538): Do not infer from Shipping API level. 481 if (shippingApiLevel) { 482 std::cerr << "Warning: Shipping FCM Version is inferred from Shipping API level. " 483 << "Declare Shipping FCM Version in device manifest directly." << std::endl; 484 manifest->mLevel = details::convertFromApiLevel(shippingApiLevel); 485 if (manifest->mLevel == Level::UNSPECIFIED) { 486 std::cerr << "Error: Shipping FCM Version cannot be inferred from Shipping API " 487 << "level " << shippingApiLevel << "." 488 << "Declare Shipping FCM Version in device manifest directly." 489 << std::endl; 490 return false; 491 } 492 return true; 493 } 494 // TODO(b/69638851): should be an error if Shipping API level is not defined. 495 // For now, just leave it empty; when framework compatibility matrix is built, 496 // lowest FCM Version is assumed. 497 std::cerr << "Warning: Shipping FCM Version cannot be inferred, because:" << std::endl 498 << " (1) It is not explicitly declared in device manifest;" << std::endl 499 << " (2) PRODUCT_ENFORCE_VINTF_MANIFEST is set to true;" << std::endl 500 << " (3) PRODUCT_SHIPPING_API_LEVEL is undefined." << std::endl 501 << "Assuming 'unspecified' Shipping FCM Version. " << std::endl 502 << "To remove this warning, define 'level' attribute in device manifest." 503 << std::endl; 504 return true; 505 } 506 507 Level getLowestFcmVersion(const CompatibilityMatrices& matrices) { 508 Level ret = Level::UNSPECIFIED; 509 for (const auto& e : matrices) { 510 if (ret == Level::UNSPECIFIED || ret > e.level()) { 511 ret = e.level(); 512 } 513 } 514 return ret; 515 } 516 517 bool assembleCompatibilityMatrix(CompatibilityMatrices* matrices) { 518 std::string error; 519 CompatibilityMatrix* matrix = nullptr; 520 std::unique_ptr<HalManifest> checkManifest; 521 std::unique_ptr<CompatibilityMatrix> builtMatrix; 522 523 if (mCheckFile.hasStream()) { 524 checkManifest = std::make_unique<HalManifest>(); 525 checkManifest->setFileName(mCheckFile.name()); 526 if (!fromXml(checkManifest.get(), read(mCheckFile.stream()), &error)) { 527 std::cerr << "Cannot parse check file as a HAL manifest: " << error << std::endl; 528 return false; 529 } 530 } 531 532 if (matrices->front().mType == SchemaType::DEVICE) { 533 builtMatrix = CompatibilityMatrix::combineDeviceMatrices(matrices, &error); 534 matrix = builtMatrix.get(); 535 536 if (matrix == nullptr) { 537 std::cerr << error << std::endl; 538 return false; 539 } 540 541 auto vndkVersion = base::Trim(getEnv("REQUIRED_VNDK_VERSION")); 542 if (!vndkVersion.empty()) { 543 auto& valueInMatrix = matrix->device.mVendorNdk; 544 if (!valueInMatrix.version().empty() && valueInMatrix.version() != vndkVersion) { 545 std::cerr << "Hard-coded <vendor-ndk> version in device compatibility matrix (" 546 << matrices->front().fileName() << "), '" << valueInMatrix.version() 547 << "', does not match value inferred " 548 << "from BOARD_VNDK_VERSION '" << vndkVersion << "'" << std::endl; 549 return false; 550 } 551 valueInMatrix = VendorNdk{std::move(vndkVersion)}; 552 } 553 554 for (auto&& v : getEnvList("BOARD_SYSTEMSDK_VERSIONS")) { 555 matrix->device.mSystemSdk.mVersions.emplace(std::move(v)); 556 } 557 } 558 559 if (matrices->front().mType == SchemaType::FRAMEWORK) { 560 Level deviceLevel = 561 checkManifest != nullptr ? checkManifest->level() : Level::UNSPECIFIED; 562 if (deviceLevel == Level::UNSPECIFIED) { 563 deviceLevel = getLowestFcmVersion(*matrices); 564 if (checkManifest != nullptr && deviceLevel != Level::UNSPECIFIED) { 565 std::cerr << "Warning: No Target FCM Version for device. Assuming \"" 566 << to_string(deviceLevel) 567 << "\" when building final framework compatibility matrix." 568 << std::endl; 569 } 570 } 571 builtMatrix = CompatibilityMatrix::combine(deviceLevel, matrices, &error); 572 matrix = builtMatrix.get(); 573 574 if (matrix == nullptr) { 575 std::cerr << error << std::endl; 576 return false; 577 } 578 579 if (!assembleFrameworkCompatibilityMatrixKernels(matrix)) { 580 return false; 581 } 582 583 // Add PLATFORM_SEPOLICY_* to sepolicy.sepolicy-version. Remove dupes. 584 std::set<Version> sepolicyVersions; 585 auto sepolicyVersionStrings = getEnvList("PLATFORM_SEPOLICY_COMPAT_VERSIONS"); 586 auto currentSepolicyVersionString = getEnv("PLATFORM_SEPOLICY_VERSION"); 587 if (!currentSepolicyVersionString.empty()) { 588 sepolicyVersionStrings.push_back(currentSepolicyVersionString); 589 } 590 for (auto&& s : sepolicyVersionStrings) { 591 Version v; 592 if (!parse(s, &v)) { 593 std::cerr << "Error: unknown sepolicy version '" << s << "' specified by " 594 << (s == currentSepolicyVersionString 595 ? "PLATFORM_SEPOLICY_VERSION" 596 : "PLATFORM_SEPOLICY_COMPAT_VERSIONS") 597 << "."; 598 return false; 599 } 600 sepolicyVersions.insert(v); 601 } 602 for (auto&& v : sepolicyVersions) { 603 matrix->framework.mSepolicy.mSepolicyVersionRanges.emplace_back(v.majorVer, 604 v.minorVer); 605 } 606 607 if (!getFlagIfUnset("POLICYVERS", 608 &matrix->framework.mSepolicy.mKernelSepolicyVersion)) { 609 return false; 610 } 611 if (!getFlagIfUnset("FRAMEWORK_VBMETA_VERSION", &matrix->framework.mAvbMetaVersion)) { 612 return false; 613 } 614 // Hard-override existing AVB version 615 getFlag("FRAMEWORK_VBMETA_VERSION_OVERRIDE", &matrix->framework.mAvbMetaVersion, 616 false /* log */); 617 } 618 outputInputs(*matrices); 619 out() << toXml(*matrix, mSerializeFlags); 620 out().flush(); 621 622 if (checkManifest != nullptr && !checkDualFile(*checkManifest, *matrix)) { 623 return false; 624 } 625 626 return true; 627 } 628 629 enum AssembleStatus { SUCCESS, FAIL_AND_EXIT, TRY_NEXT }; 630 template <typename Schema, typename AssembleFunc> 631 AssembleStatus tryAssemble(const std::string& schemaName, AssembleFunc assemble, 632 std::string* error) { 633 std::vector<Schema> schemas; 634 Schema schema; 635 schema.setFileName(mInFiles.front().name()); 636 if (!fromXml(&schema, read(mInFiles.front().stream()), error)) { 637 return TRY_NEXT; 638 } 639 auto firstType = schema.type(); 640 schemas.emplace_back(std::move(schema)); 641 642 for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) { 643 Schema additionalSchema; 644 const std::string& fileName = it->name(); 645 additionalSchema.setFileName(fileName); 646 if (!fromXml(&additionalSchema, read(it->stream()), error)) { 647 std::cerr << "File \"" << fileName << "\" is not a valid " << firstType << " " 648 << schemaName << " (but the first file is a valid " << firstType << " " 649 << schemaName << "). Error: " << *error << std::endl; 650 return FAIL_AND_EXIT; 651 } 652 if (additionalSchema.type() != firstType) { 653 std::cerr << "File \"" << fileName << "\" is a " << additionalSchema.type() << " " 654 << schemaName << " (but a " << firstType << " " << schemaName 655 << " is expected)." << std::endl; 656 return FAIL_AND_EXIT; 657 } 658 659 schemas.emplace_back(std::move(additionalSchema)); 660 } 661 return assemble(&schemas) ? SUCCESS : FAIL_AND_EXIT; 662 } 663 664 bool assemble() override { 665 using std::placeholders::_1; 666 if (mInFiles.empty()) { 667 std::cerr << "Missing input file." << std::endl; 668 return false; 669 } 670 671 std::string manifestError; 672 auto status = tryAssemble<HalManifest>( 673 "manifest", std::bind(&AssembleVintfImpl::assembleHalManifest, this, _1), 674 &manifestError); 675 if (status == SUCCESS) return true; 676 if (status == FAIL_AND_EXIT) return false; 677 678 resetInFiles(); 679 680 std::string matrixError; 681 status = tryAssemble<CompatibilityMatrix>( 682 "compatibility matrix", 683 std::bind(&AssembleVintfImpl::assembleCompatibilityMatrix, this, _1), &matrixError); 684 if (status == SUCCESS) return true; 685 if (status == FAIL_AND_EXIT) return false; 686 687 std::cerr << "Input file has unknown format." << std::endl 688 << "Error when attempting to convert to manifest: " << manifestError << std::endl 689 << "Error when attempting to convert to compatibility matrix: " << matrixError 690 << std::endl; 691 return false; 692 } 693 694 std::ostream& setOutputStream(Ostream&& out) override { 695 mOutRef = std::move(out); 696 return *mOutRef; 697 } 698 699 std::istream& addInputStream(const std::string& name, Istream&& in) override { 700 auto it = mInFiles.emplace(mInFiles.end(), name, std::move(in)); 701 return it->stream(); 702 } 703 704 std::istream& setCheckInputStream(const std::string& name, Istream&& in) override { 705 mCheckFile = NamedIstream(name, std::move(in)); 706 return mCheckFile.stream(); 707 } 708 709 bool hasKernelVersion(const KernelVersion& kernelVer) const override { 710 return mKernels.find(kernelVer) != mKernels.end(); 711 } 712 713 std::istream& addKernelConfigInputStream(const KernelVersion& kernelVer, 714 const std::string& name, Istream&& in) override { 715 auto&& kernel = mKernels[kernelVer]; 716 auto it = kernel.emplace(kernel.end(), name, std::move(in)); 717 return it->stream(); 718 } 719 720 void resetInFiles() { 721 for (auto& inFile : mInFiles) { 722 inFile.stream().clear(); 723 inFile.stream().seekg(0); 724 } 725 } 726 727 void setOutputMatrix() override { mOutputMatrix = true; } 728 729 bool setHalsOnly() override { 730 if (mHasSetHalsOnlyFlag) { 731 std::cerr << "Error: Cannot set --hals-only with --no-hals." << std::endl; 732 return false; 733 } 734 // Just override it with HALS_ONLY because other flags that modify mSerializeFlags 735 // does not interfere with this (except --no-hals). 736 mSerializeFlags = SerializeFlags::HALS_ONLY; 737 mHasSetHalsOnlyFlag = true; 738 return true; 739 } 740 741 bool setNoHals() override { 742 if (mHasSetHalsOnlyFlag) { 743 std::cerr << "Error: Cannot set --hals-only with --no-hals." << std::endl; 744 return false; 745 } 746 mSerializeFlags = mSerializeFlags.disableHals(); 747 mHasSetHalsOnlyFlag = true; 748 return true; 749 } 750 751 bool setNoKernelRequirements() override { 752 mSerializeFlags = mSerializeFlags.disableKernelConfigs().disableKernelMinorRevision(); 753 mCheckFlags = mCheckFlags.disableKernel(); 754 return true; 755 } 756 757 private: 758 std::vector<NamedIstream> mInFiles; 759 Ostream mOutRef; 760 NamedIstream mCheckFile; 761 bool mOutputMatrix = false; 762 bool mHasSetHalsOnlyFlag = false; 763 SerializeFlags::Type mSerializeFlags = SerializeFlags::EVERYTHING; 764 std::map<KernelVersion, std::vector<NamedIstream>> mKernels; 765 std::map<std::string, std::string> mFakeEnv; 766 CheckFlags::Type mCheckFlags = CheckFlags::DEFAULT; 767 }; 768 769 bool AssembleVintf::openOutFile(const std::string& path) { 770 return static_cast<std::ofstream&>(setOutputStream(std::make_unique<std::ofstream>(path))) 771 .is_open(); 772 } 773 774 bool AssembleVintf::openInFile(const std::string& path) { 775 return static_cast<std::ifstream&>(addInputStream(path, std::make_unique<std::ifstream>(path))) 776 .is_open(); 777 } 778 779 bool AssembleVintf::openCheckFile(const std::string& path) { 780 return static_cast<std::ifstream&>( 781 setCheckInputStream(path, std::make_unique<std::ifstream>(path))) 782 .is_open(); 783 } 784 785 bool AssembleVintf::addKernel(const std::string& kernelArg) { 786 auto tokens = base::Split(kernelArg, ":"); 787 if (tokens.size() <= 1) { 788 std::cerr << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl; 789 return false; 790 } 791 KernelVersion kernelVer; 792 if (!parse(tokens.front(), &kernelVer)) { 793 std::cerr << "Unrecognized kernel version '" << tokens.front() << "'" << std::endl; 794 return false; 795 } 796 if (hasKernelVersion(kernelVer)) { 797 std::cerr << "Multiple --kernel for " << kernelVer << " is specified." << std::endl; 798 return false; 799 } 800 for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { 801 bool opened = 802 static_cast<std::ifstream&>( 803 addKernelConfigInputStream(kernelVer, *it, std::make_unique<std::ifstream>(*it))) 804 .is_open(); 805 if (!opened) { 806 std::cerr << "Cannot open file '" << *it << "'." << std::endl; 807 return false; 808 } 809 } 810 return true; 811 } 812 813 std::unique_ptr<AssembleVintf> AssembleVintf::newInstance() { 814 return std::make_unique<AssembleVintfImpl>(); 815 } 816 817 } // namespace vintf 818 } // namespace android 819