AssembleVintf.cpp revision 8302cea08dea5238cbc4d2637ff90319480971ae
1a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich/* 2a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * Copyright (C) 2017 The Android Open Source Project 3a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * 4a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * Licensed under the Apache License, Version 2.0 (the "License"); 5a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * you may not use this file except in compliance with the License. 6a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * You may obtain a copy of the License at 7a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * 8a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * http://www.apache.org/licenses/LICENSE-2.0 9a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * 10a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * Unless required by applicable law or agreed to in writing, software 11a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * distributed under the License is distributed on an "AS IS" BASIS, 12a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * See the License for the specific language governing permissions and 14a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich * limitations under the License. 15a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich */ 16a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich 17a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich#include <stdlib.h> 18a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich#include <unistd.h> 19655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng 20655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng#include <fstream> 21655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng#include <iostream> 22655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng#include <sstream> 23d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao#include <string> 24d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao#include <unordered_map> 25d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao 26d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao#include <android-base/file.h> 27d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao#include <android-base/parseint.h> 28d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao 29655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng#include <vintf/AssembleVintf.h> 30655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng#include <vintf/KernelConfigParser.h> 31655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng#include <vintf/parse_string.h> 32655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng#include <vintf/parse_xml.h> 33d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao#include "utils.h" 34d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao 35d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao#define BUFFER_SIZE sysconf(_SC_PAGESIZE) 36d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao 37d7db594b8d1dab36b711bd887a9dd21675c87243Tao Baonamespace android { 38d7db594b8d1dab36b711bd887a9dd21675c87243Tao Baonamespace vintf { 39d7db594b8d1dab36b711bd887a9dd21675c87243Tao Bao 40d7db594b8d1dab36b711bd887a9dd21675c87243Tao Baostatic const std::string gConfigPrefix = "android-base-"; 41d7db594b8d1dab36b711bd887a9dd21675c87243Tao Baostatic const std::string gConfigSuffix = ".cfg"; 42655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Chengstatic const std::string gBaseConfig = "android-base.cfg"; 43655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng 44655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng// An input stream with a name. 45655a7c081f83b8351ed5f11a6c6accd9458293a8Ben Cheng// The input stream may be an actual file, or a stringstream for testing. 46a67e4de6620b570dabe0f92985228af0d0204f2cNick Kralevich// It takes ownership on the istream. 47class NamedIstream { 48 public: 49 NamedIstream(const std::string& name, std::unique_ptr<std::istream>&& stream) 50 : mName(name), mStream(std::move(stream)) {} 51 const std::string& name() const { return mName; } 52 std::istream& stream() { return *mStream; } 53 54 private: 55 std::string mName; 56 std::unique_ptr<std::istream> mStream; 57}; 58 59/** 60 * Slurps the device manifest file and add build time flag to it. 61 */ 62class AssembleVintfImpl : public AssembleVintf { 63 using Condition = std::unique_ptr<KernelConfig>; 64 using ConditionedConfig = std::pair<Condition, std::vector<KernelConfig> /* configs */>; 65 66 public: 67 void setFakeEnv(const std::string& key, const std::string& value) { mFakeEnv[key] = value; } 68 69 std::string getEnv(const std::string& key) const { 70 auto it = mFakeEnv.find(key); 71 if (it != mFakeEnv.end()) { 72 return it->second; 73 } 74 const char* envValue = getenv(key.c_str()); 75 return envValue != nullptr ? std::string(envValue) : std::string(); 76 } 77 78 template <typename T> 79 bool getFlag(const std::string& key, T* value) const { 80 std::string envValue = getEnv(key); 81 if (envValue.empty()) { 82 std::cerr << "Warning: " << key << " is missing, defaulted to " << (*value) << "." 83 << std::endl; 84 return true; 85 } 86 87 if (!parse(envValue, value)) { 88 std::cerr << "Cannot parse " << envValue << "." << std::endl; 89 return false; 90 } 91 return true; 92 } 93 94 /** 95 * Set *out to environment variable if *out is not a dummy value (i.e. default constructed). 96 */ 97 template <typename T> 98 bool getFlagIfUnset(const std::string& envKey, T* out) const { 99 bool hasExistingValue = !(*out == T{}); 100 101 bool hasEnvValue = false; 102 T envValue; 103 std::string envStrValue = getEnv(envKey); 104 if (!envStrValue.empty()) { 105 if (!parse(envStrValue, &envValue)) { 106 std::cerr << "Cannot parse " << envValue << "." << std::endl; 107 return false; 108 } 109 hasEnvValue = true; 110 } 111 112 if (hasExistingValue) { 113 if (hasEnvValue) { 114 std::cerr << "Warning: cannot override existing value " << *out << " with " 115 << envKey << " (which is " << envValue << ")." << std::endl; 116 } 117 return false; 118 } 119 if (!hasEnvValue) { 120 std::cerr << "Warning: " << envKey << " is not specified. Default to " << T{} << "." 121 << std::endl; 122 return false; 123 } 124 *out = envValue; 125 return true; 126 } 127 128 bool getBooleanFlag(const std::string& key) const { return getEnv(key) == std::string("true"); } 129 130 size_t getIntegerFlag(const std::string& key, size_t defaultValue = 0) const { 131 std::string envValue = getEnv(key); 132 if (envValue.empty()) { 133 return defaultValue; 134 } 135 size_t value; 136 if (!base::ParseUint(envValue, &value)) { 137 std::cerr << "Error: " << key << " must be a number." << std::endl; 138 return defaultValue; 139 } 140 return value; 141 } 142 143 static std::string read(std::basic_istream<char>& is) { 144 std::stringstream ss; 145 ss << is.rdbuf(); 146 return ss.str(); 147 } 148 149 static bool isCommonConfig(const std::string& path) { 150 return ::android::base::Basename(path) == gBaseConfig; 151 } 152 153 static Level convertFromApiLevel(size_t apiLevel) { 154 if (apiLevel < 26) { 155 return Level::LEGACY; 156 } else if (apiLevel == 26) { 157 return Level::O; 158 } else if (apiLevel == 27) { 159 return Level::O_MR1; 160 } else { 161 return Level::UNSPECIFIED; 162 } 163 } 164 165 // nullptr on any error, otherwise the condition. 166 static Condition generateCondition(const std::string& path) { 167 std::string fname = ::android::base::Basename(path); 168 if (fname.size() <= gConfigPrefix.size() + gConfigSuffix.size() || 169 !std::equal(gConfigPrefix.begin(), gConfigPrefix.end(), fname.begin()) || 170 !std::equal(gConfigSuffix.rbegin(), gConfigSuffix.rend(), fname.rbegin())) { 171 return nullptr; 172 } 173 174 std::string sub = fname.substr(gConfigPrefix.size(), 175 fname.size() - gConfigPrefix.size() - gConfigSuffix.size()); 176 if (sub.empty()) { 177 return nullptr; // should not happen 178 } 179 for (size_t i = 0; i < sub.size(); ++i) { 180 if (sub[i] == '-') { 181 sub[i] = '_'; 182 continue; 183 } 184 if (isalnum(sub[i])) { 185 sub[i] = toupper(sub[i]); 186 continue; 187 } 188 std::cerr << "'" << fname << "' (in " << path 189 << ") is not a valid kernel config file name. Must match regex: " 190 << "android-base(-[0-9a-zA-Z-]+)?\\.cfg" << std::endl; 191 return nullptr; 192 } 193 sub.insert(0, "CONFIG_"); 194 return std::make_unique<KernelConfig>(std::move(sub), Tristate::YES); 195 } 196 197 static bool parseFileForKernelConfigs(std::basic_istream<char>& stream, 198 std::vector<KernelConfig>* out) { 199 KernelConfigParser parser(true /* processComments */, true /* relaxedFormat */); 200 std::string content = read(stream); 201 status_t err = parser.process(content.c_str(), content.size()); 202 if (err != OK) { 203 std::cerr << parser.error(); 204 return false; 205 } 206 err = parser.finish(); 207 if (err != OK) { 208 std::cerr << parser.error(); 209 return false; 210 } 211 212 for (auto& configPair : parser.configs()) { 213 out->push_back({}); 214 KernelConfig& config = out->back(); 215 config.first = std::move(configPair.first); 216 if (!parseKernelConfigTypedValue(configPair.second, &config.second)) { 217 std::cerr << "Unknown value type for key = '" << config.first << "', value = '" 218 << configPair.second << "'\n"; 219 return false; 220 } 221 } 222 return true; 223 } 224 225 static bool parseFilesForKernelConfigs(std::vector<NamedIstream>* streams, 226 std::vector<ConditionedConfig>* out) { 227 out->clear(); 228 ConditionedConfig commonConfig; 229 bool foundCommonConfig = false; 230 bool ret = true; 231 232 for (auto& namedStream : *streams) { 233 if (isCommonConfig(namedStream.name())) { 234 ret &= parseFileForKernelConfigs(namedStream.stream(), &commonConfig.second); 235 foundCommonConfig = true; 236 } else { 237 Condition condition = generateCondition(namedStream.name()); 238 ret &= (condition != nullptr); 239 240 std::vector<KernelConfig> kernelConfigs; 241 if ((ret &= parseFileForKernelConfigs(namedStream.stream(), &kernelConfigs))) 242 out->emplace_back(std::move(condition), std::move(kernelConfigs)); 243 } 244 } 245 246 if (!foundCommonConfig) { 247 std::cerr << "No android-base.cfg is found in these paths:" << std::endl; 248 for (auto& namedStream : *streams) { 249 std::cerr << " " << namedStream.name() << std::endl; 250 } 251 } 252 ret &= foundCommonConfig; 253 // first element is always common configs (no conditions). 254 out->insert(out->begin(), std::move(commonConfig)); 255 return ret; 256 } 257 258 static std::string getFileNameFromPath(std::string path) { 259 auto idx = path.find_last_of("\\/"); 260 if (idx != std::string::npos) { 261 path.erase(0, idx + 1); 262 } 263 return path; 264 } 265 266 std::basic_ostream<char>& out() const { return mOutRef == nullptr ? std::cout : *mOutRef; } 267 268 template <typename S> 269 using Schemas = std::vector<std::pair<std::string, S>>; 270 using HalManifests = Schemas<HalManifest>; 271 using CompatibilityMatrices = Schemas<CompatibilityMatrix>; 272 273 bool assembleHalManifest(HalManifests* halManifests) { 274 std::string error; 275 HalManifest* halManifest = &halManifests->front().second; 276 for (auto it = halManifests->begin() + 1; it != halManifests->end(); ++it) { 277 const std::string& path = it->first; 278 HalManifest& halToAdd = it->second; 279 280 if (halToAdd.level() != Level::UNSPECIFIED) { 281 if (halManifest->level() == Level::UNSPECIFIED) { 282 halManifest->mLevel = halToAdd.level(); 283 } else if (halManifest->level() != halToAdd.level()) { 284 std::cerr << "Inconsistent FCM Version in HAL manifests:" << std::endl 285 << " File '" << halManifests->front().first << "' has level " 286 << halManifest->level() << std::endl 287 << " File '" << path << "' has level " << halToAdd.level() 288 << std::endl; 289 return false; 290 } 291 } 292 293 if (!halManifest->addAllHals(&halToAdd, &error)) { 294 std::cerr << "File \"" << path << "\" cannot be added: conflict on HAL \"" << error 295 << "\" with an existing HAL. See <hal> with the same name " 296 << "in previously parsed files or previously declared in this file." 297 << std::endl; 298 return false; 299 } 300 } 301 302 if (halManifest->mType == SchemaType::DEVICE) { 303 if (!getFlag("BOARD_SEPOLICY_VERS", &halManifest->device.mSepolicyVersion)) { 304 return false; 305 } 306 if (!setDeviceFcmVersion(halManifest)) { 307 return false; 308 } 309 } 310 311 if (mOutputMatrix) { 312 CompatibilityMatrix generatedMatrix = halManifest->generateCompatibleMatrix(); 313 if (!halManifest->checkCompatibility(generatedMatrix, &error)) { 314 std::cerr << "FATAL ERROR: cannot generate a compatible matrix: " << error 315 << std::endl; 316 } 317 out() << "<!-- \n" 318 " Autogenerated skeleton compatibility matrix. \n" 319 " Use with caution. Modify it to suit your needs.\n" 320 " All HALs are set to optional.\n" 321 " Many entries other than HALs are zero-filled and\n" 322 " require human attention. \n" 323 "-->\n" 324 << gCompatibilityMatrixConverter(generatedMatrix, mSerializeFlags); 325 } else { 326 out() << gHalManifestConverter(*halManifest, mSerializeFlags); 327 } 328 out().flush(); 329 330 if (mCheckFile != nullptr) { 331 CompatibilityMatrix checkMatrix; 332 if (!gCompatibilityMatrixConverter(&checkMatrix, read(*mCheckFile))) { 333 std::cerr << "Cannot parse check file as a compatibility matrix: " 334 << gCompatibilityMatrixConverter.lastError() << std::endl; 335 return false; 336 } 337 if (!halManifest->checkCompatibility(checkMatrix, &error)) { 338 std::cerr << "Not compatible: " << error << std::endl; 339 return false; 340 } 341 } 342 343 return true; 344 } 345 346 bool assembleFrameworkCompatibilityMatrixKernels(CompatibilityMatrix* matrix) { 347 for (auto& pair : mKernels) { 348 std::vector<ConditionedConfig> conditionedConfigs; 349 if (!parseFilesForKernelConfigs(&pair.second, &conditionedConfigs)) { 350 return false; 351 } 352 for (ConditionedConfig& conditionedConfig : conditionedConfigs) { 353 MatrixKernel kernel(KernelVersion{pair.first.majorVer, pair.first.minorVer, 0u}, 354 std::move(conditionedConfig.second)); 355 if (conditionedConfig.first != nullptr) 356 kernel.mConditions.push_back(std::move(*conditionedConfig.first)); 357 matrix->framework.mKernels.push_back(std::move(kernel)); 358 } 359 } 360 return true; 361 } 362 363 bool setDeviceFcmVersion(HalManifest* manifest) { 364 size_t shippingApiLevel = getIntegerFlag("PRODUCT_SHIPPING_API_LEVEL"); 365 366 if (manifest->level() != Level::UNSPECIFIED) { 367 return true; 368 } 369 if (!getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST")) { 370 manifest->mLevel = Level::LEGACY; 371 return true; 372 } 373 // TODO(b/70628538): Do not infer from Shipping API level. 374 if (shippingApiLevel) { 375 std::cerr << "Warning: Shipping FCM Version is inferred from Shipping API level. " 376 << "Declare Shipping FCM Version in device manifest directly." << std::endl; 377 manifest->mLevel = convertFromApiLevel(shippingApiLevel); 378 if (manifest->mLevel == Level::UNSPECIFIED) { 379 std::cerr << "Error: Shipping FCM Version cannot be inferred from Shipping API " 380 << "level " << shippingApiLevel << "." 381 << "Declare Shipping FCM Version in device manifest directly." 382 << std::endl; 383 return false; 384 } 385 return true; 386 } 387 // TODO(b/69638851): should be an error if Shipping API level is not defined. 388 // For now, just leave it empty; when framework compatibility matrix is built, 389 // lowest FCM Version is assumed. 390 std::cerr << "Warning: Shipping FCM Version cannot be inferred, because:" << std::endl 391 << " (1) It is not explicitly declared in device manifest;" << std::endl 392 << " (2) PRODUCT_ENFORCE_VINTF_MANIFEST is set to true;" << std::endl 393 << " (3) PRODUCT_SHIPPING_API_LEVEL is undefined." << std::endl 394 << "Assuming 'unspecified' Shipping FCM Version. " << std::endl 395 << "To remove this warning, define 'level' attribute in device manifest." 396 << std::endl; 397 return true; 398 } 399 400 Level getLowestFcmVersion(const CompatibilityMatrices& matrices) { 401 Level ret = Level::UNSPECIFIED; 402 for (const auto& e : matrices) { 403 if (ret == Level::UNSPECIFIED || ret > e.second.level()) { 404 ret = e.second.level(); 405 } 406 } 407 return ret; 408 } 409 410 bool assembleCompatibilityMatrix(CompatibilityMatrices* matrices) { 411 std::string error; 412 CompatibilityMatrix* matrix = nullptr; 413 KernelSepolicyVersion kernelSepolicyVers; 414 Version sepolicyVers; 415 std::unique_ptr<HalManifest> checkManifest; 416 if (matrices->front().second.mType == SchemaType::DEVICE) { 417 matrix = &matrices->front().second; 418 } 419 420 if (matrices->front().second.mType == SchemaType::FRAMEWORK) { 421 Level deviceLevel = Level::UNSPECIFIED; 422 std::vector<std::string> fileList; 423 if (mCheckFile != nullptr) { 424 checkManifest = std::make_unique<HalManifest>(); 425 if (!gHalManifestConverter(checkManifest.get(), read(*mCheckFile))) { 426 std::cerr << "Cannot parse check file as a HAL manifest: " 427 << gHalManifestConverter.lastError() << std::endl; 428 return false; 429 } 430 deviceLevel = checkManifest->level(); 431 } 432 433 if (deviceLevel == Level::UNSPECIFIED) { 434 // For GSI build, legacy devices that do not have a HAL manifest, 435 // and devices in development, merge all compatibility matrices. 436 deviceLevel = getLowestFcmVersion(*matrices); 437 } 438 439 for (auto& e : *matrices) { 440 if (e.second.level() == deviceLevel) { 441 fileList.push_back(e.first); 442 matrix = &e.second; 443 } 444 } 445 if (matrix == nullptr) { 446 std::cerr << "FATAL ERROR: cannot find matrix with level '" << deviceLevel << "'" 447 << std::endl; 448 return false; 449 } 450 for (auto& e : *matrices) { 451 if (e.second.level() <= deviceLevel) { 452 continue; 453 } 454 fileList.push_back(e.first); 455 if (!matrix->addAllHalsAsOptional(&e.second, &error)) { 456 std::cerr << "File \"" << e.first << "\" cannot be added: " << error 457 << ". See <hal> with the same name " 458 << "in previously parsed files or previously declared in this file." 459 << std::endl; 460 return false; 461 } 462 } 463 464 if (!getFlag("BOARD_SEPOLICY_VERS", &sepolicyVers)) { 465 return false; 466 } 467 if (!getFlag("POLICYVERS", &kernelSepolicyVers)) { 468 return false; 469 } 470 471 if (!assembleFrameworkCompatibilityMatrixKernels(matrix)) { 472 return false; 473 } 474 475 matrix->framework.mSepolicy = 476 Sepolicy(kernelSepolicyVers, {{sepolicyVers.majorVer, sepolicyVers.minorVer}}); 477 478 Version avbMetaVersion; 479 if (!getFlag("FRAMEWORK_VBMETA_VERSION", &avbMetaVersion)) { 480 return false; 481 } 482 matrix->framework.mAvbMetaVersion = avbMetaVersion; 483 484 out() << "<!--" << std::endl; 485 out() << " Input:" << std::endl; 486 for (const auto& path : fileList) { 487 out() << " " << getFileNameFromPath(path) << std::endl; 488 } 489 out() << "-->" << std::endl; 490 } 491 out() << gCompatibilityMatrixConverter(*matrix, mSerializeFlags); 492 out().flush(); 493 494 if (checkManifest != nullptr && getBooleanFlag("PRODUCT_ENFORCE_VINTF_MANIFEST") && 495 !checkManifest->checkCompatibility(*matrix, &error)) { 496 std::cerr << "Not compatible: " << error << std::endl; 497 return false; 498 } 499 500 return true; 501 } 502 503 enum AssembleStatus { SUCCESS, FAIL_AND_EXIT, TRY_NEXT }; 504 template <typename Schema, typename AssembleFunc> 505 AssembleStatus tryAssemble(const XmlConverter<Schema>& converter, const std::string& schemaName, 506 AssembleFunc assemble) { 507 Schemas<Schema> schemas; 508 Schema schema; 509 if (!converter(&schema, read(mInFiles.front().stream()))) { 510 return TRY_NEXT; 511 } 512 auto firstType = schema.type(); 513 schemas.emplace_back(mInFiles.front().name(), std::move(schema)); 514 515 for (auto it = mInFiles.begin() + 1; it != mInFiles.end(); ++it) { 516 Schema additionalSchema; 517 const std::string& fileName = it->name(); 518 if (!converter(&additionalSchema, read(it->stream()))) { 519 std::cerr << "File \"" << fileName << "\" is not a valid " << firstType << " " 520 << schemaName << " (but the first file is a valid " << firstType << " " 521 << schemaName << "). Error: " << converter.lastError() << std::endl; 522 return FAIL_AND_EXIT; 523 } 524 if (additionalSchema.type() != firstType) { 525 std::cerr << "File \"" << fileName << "\" is a " << additionalSchema.type() << " " 526 << schemaName << " (but a " << firstType << " " << schemaName 527 << " is expected)." << std::endl; 528 return FAIL_AND_EXIT; 529 } 530 531 schemas.emplace_back(fileName, std::move(additionalSchema)); 532 } 533 return assemble(&schemas) ? SUCCESS : FAIL_AND_EXIT; 534 } 535 536 bool assemble() override { 537 using std::placeholders::_1; 538 if (mInFiles.empty()) { 539 std::cerr << "Missing input file." << std::endl; 540 return false; 541 } 542 543 auto status = tryAssemble(gHalManifestConverter, "manifest", 544 std::bind(&AssembleVintfImpl::assembleHalManifest, this, _1)); 545 if (status == SUCCESS) return true; 546 if (status == FAIL_AND_EXIT) return false; 547 548 resetInFiles(); 549 550 status = tryAssemble(gCompatibilityMatrixConverter, "compatibility matrix", 551 std::bind(&AssembleVintfImpl::assembleCompatibilityMatrix, this, _1)); 552 if (status == SUCCESS) return true; 553 if (status == FAIL_AND_EXIT) return false; 554 555 std::cerr << "Input file has unknown format." << std::endl 556 << "Error when attempting to convert to manifest: " 557 << gHalManifestConverter.lastError() << std::endl 558 << "Error when attempting to convert to compatibility matrix: " 559 << gCompatibilityMatrixConverter.lastError() << std::endl; 560 return false; 561 } 562 563 std::ostream& setOutputStream(Ostream&& out) override { 564 mOutRef = std::move(out); 565 return *mOutRef; 566 } 567 568 std::istream& addInputStream(const std::string& name, Istream&& in) override { 569 auto it = mInFiles.emplace(mInFiles.end(), name, std::move(in)); 570 return it->stream(); 571 } 572 573 std::istream& setCheckInputStream(Istream&& in) override { 574 mCheckFile = std::move(in); 575 return *mCheckFile; 576 } 577 578 bool hasKernelVersion(const Version& kernelVer) const override { 579 return mKernels.find(kernelVer) != mKernels.end(); 580 } 581 582 std::istream& addKernelConfigInputStream(const Version& kernelVer, const std::string& name, 583 Istream&& in) override { 584 auto&& kernel = mKernels[kernelVer]; 585 auto it = kernel.emplace(kernel.end(), name, std::move(in)); 586 return it->stream(); 587 } 588 589 void resetInFiles() { 590 for (auto& inFile : mInFiles) { 591 inFile.stream().clear(); 592 inFile.stream().seekg(0); 593 } 594 } 595 596 void setOutputMatrix() override { mOutputMatrix = true; } 597 598 bool setHalsOnly() override { 599 if (mSerializeFlags) return false; 600 mSerializeFlags |= SerializeFlag::HALS_ONLY; 601 return true; 602 } 603 604 bool setNoHals() override { 605 if (mSerializeFlags) return false; 606 mSerializeFlags |= SerializeFlag::NO_HALS; 607 return true; 608 } 609 610 private: 611 std::vector<NamedIstream> mInFiles; 612 Ostream mOutRef; 613 Istream mCheckFile; 614 bool mOutputMatrix = false; 615 SerializeFlags mSerializeFlags = SerializeFlag::EVERYTHING; 616 std::map<Version, std::vector<NamedIstream>> mKernels; 617 std::map<std::string, std::string> mFakeEnv; 618}; 619 620bool AssembleVintf::openOutFile(const std::string& path) { 621 return static_cast<std::ofstream&>(setOutputStream(std::make_unique<std::ofstream>(path))) 622 .is_open(); 623} 624 625bool AssembleVintf::openInFile(const std::string& path) { 626 return static_cast<std::ifstream&>(addInputStream(path, std::make_unique<std::ifstream>(path))) 627 .is_open(); 628} 629 630bool AssembleVintf::openCheckFile(const std::string& path) { 631 return static_cast<std::ifstream&>(setCheckInputStream(std::make_unique<std::ifstream>(path))) 632 .is_open(); 633} 634 635bool AssembleVintf::addKernel(const std::string& kernelArg) { 636 auto tokens = details::tokenize(kernelArg); 637 if (tokens.size() <= 1) { 638 std::cerr << "Unrecognized --kernel option '" << kernelArg << "'" << std::endl; 639 return false; 640 } 641 Version kernelVer; 642 if (!parse(tokens.front(), &kernelVer)) { 643 std::cerr << "Unrecognized kernel version '" << tokens.front() << "'" << std::endl; 644 return false; 645 } 646 if (hasKernelVersion(kernelVer)) { 647 std::cerr << "Multiple --kernel for " << kernelVer << " is specified." << std::endl; 648 return false; 649 } 650 for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { 651 bool opened = 652 static_cast<std::ifstream&>( 653 addKernelConfigInputStream(kernelVer, *it, std::make_unique<std::ifstream>(*it))) 654 .is_open(); 655 if (!opened) { 656 std::cerr << "Cannot open file '" << *it << "'." << std::endl; 657 return false; 658 } 659 } 660 return true; 661} 662 663std::unique_ptr<AssembleVintf> AssembleVintf::newInstance() { 664 return std::make_unique<AssembleVintfImpl>(); 665} 666 667} // namespace vintf 668} // namespace android 669