1 /* 2 * Copyright (C) 2016 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 "AST.h" 18 #include "Coordinator.h" 19 #include "Interface.h" 20 #include "Scope.h" 21 22 #include <android-base/logging.h> 23 #include <hidl-hash/Hash.h> 24 #include <hidl-util/FQName.h> 25 #include <hidl-util/Formatter.h> 26 #include <hidl-util/StringHelper.h> 27 #include <stdio.h> 28 #include <sys/stat.h> 29 #include <unistd.h> 30 #include <iostream> 31 #include <set> 32 #include <sstream> 33 #include <string> 34 #include <vector> 35 36 using namespace android; 37 38 enum class OutputMode { 39 NEEDS_DIR, // -o output option expects a directory 40 NEEDS_FILE, // -o output option expects a file 41 NEEDS_SRC, // for changes inside the source tree itself 42 NOT_NEEDED // does not create files 43 }; 44 45 enum class GenerationGranularity { 46 PER_PACKAGE, // Files generated for each package 47 PER_FILE, // Files generated for each hal file 48 PER_TYPE, // Files generated for each hal file + each type in HAL files 49 }; 50 51 // Represents a file that is generated by an -L option for an FQName 52 struct FileGenerator { 53 using ShouldGenerateFunction = std::function<bool(const FQName& fqName)>; 54 using FileNameForFQName = std::function<std::string(const FQName& fqName)>; 55 using GetFormatter = std::function<Formatter(void)>; 56 using GenerationFunction = 57 std::function<status_t(const FQName& fqName, const Coordinator* coordinator, 58 const GetFormatter& getFormatter)>; 59 60 ShouldGenerateFunction mShouldGenerateForFqName; // If generate function applies to this target 61 FileNameForFQName mFileNameForFqName; // Target -> filename 62 GenerationFunction mGenerationFunction; // Function to generate output for file 63 64 std::string getFileName(const FQName& fqName) const { 65 return mFileNameForFqName ? mFileNameForFqName(fqName) : ""; 66 } 67 68 status_t getOutputFile(const FQName& fqName, const Coordinator* coordinator, 69 Coordinator::Location location, std::string* file) const { 70 if (!mShouldGenerateForFqName(fqName)) { 71 return OK; 72 } 73 74 return coordinator->getFilepath(fqName, location, getFileName(fqName), file); 75 } 76 77 status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator, 78 Coordinator::Location location, 79 std::vector<std::string>* outputFiles) const { 80 if (location == Coordinator::Location::STANDARD_OUT) { 81 return OK; 82 } 83 84 if (mShouldGenerateForFqName(fqName)) { 85 std::string fileName; 86 status_t err = getOutputFile(fqName, coordinator, location, &fileName); 87 if (err != OK) return err; 88 89 if (!fileName.empty()) { 90 outputFiles->push_back(fileName); 91 } 92 } 93 return OK; 94 } 95 96 status_t generate(const FQName& fqName, const Coordinator* coordinator, 97 Coordinator::Location location) const { 98 CHECK(mShouldGenerateForFqName != nullptr); 99 CHECK(mGenerationFunction != nullptr); 100 101 if (!mShouldGenerateForFqName(fqName)) { 102 return OK; 103 } 104 105 return mGenerationFunction(fqName, coordinator, [&] { 106 return coordinator->getFormatter(fqName, location, getFileName(fqName)); 107 }); 108 } 109 110 // Helper methods for filling out this struct 111 static bool generateForTypes(const FQName& fqName) { 112 const auto names = fqName.names(); 113 return names.size() > 0 && names[0] == "types"; 114 } 115 static bool generateForInterfaces(const FQName& fqName) { return !generateForTypes(fqName); } 116 static bool alwaysGenerate(const FQName&) { return true; } 117 }; 118 119 // Represents a -L option, takes a fqName and generates files 120 struct OutputHandler { 121 using ValidationFunction = std::function<bool( 122 const FQName& fqName, const Coordinator* coordinator, const std::string& language)>; 123 124 std::string mKey; // -L in Android.bp 125 std::string mDescription; // for display in help menu 126 OutputMode mOutputMode; // how this option interacts with -o 127 Coordinator::Location mLocation; // how to compute location relative to the output directory 128 GenerationGranularity mGenerationGranularity; // what to run generate function on 129 ValidationFunction mValidate; // if a given fqName is allowed for this option 130 std::vector<FileGenerator> mGenerateFunctions; // run for each target at this granularity 131 132 const std::string& name() const { return mKey; } 133 const std::string& description() const { return mDescription; } 134 135 status_t generate(const FQName& fqName, const Coordinator* coordinator) const; 136 status_t validate(const FQName& fqName, const Coordinator* coordinator, 137 const std::string& language) const { 138 return mValidate(fqName, coordinator, language); 139 } 140 141 status_t writeDepFile(const FQName& fqName, const Coordinator* coordinator) const; 142 143 private: 144 status_t appendTargets(const FQName& fqName, const Coordinator* coordinator, 145 std::vector<FQName>* targets) const; 146 status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator, 147 std::vector<std::string>* outputFiles) const; 148 }; 149 150 // Helper method for GenerationGranularity::PER_TYPE 151 // IFoo -> IFoo, types.hal (containing Bar, Baz) -> types.Bar, types.Baz 152 static status_t appendPerTypeTargets(const FQName& fqName, const Coordinator* coordinator, 153 std::vector<FQName>* exportedPackageInterfaces) { 154 CHECK(fqName.isFullyQualified()); 155 if (fqName.name() != "types") { 156 exportedPackageInterfaces->push_back(fqName); 157 return OK; 158 } 159 160 AST* typesAST = coordinator->parse(fqName); 161 if (typesAST == nullptr) { 162 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); 163 return UNKNOWN_ERROR; 164 } 165 166 std::vector<NamedType*> rootTypes = typesAST->getRootScope().getSubTypes(); 167 for (const NamedType* rootType : rootTypes) { 168 if (rootType->isTypeDef()) continue; 169 170 FQName rootTypeName(fqName.package(), fqName.version(), "types." + rootType->definedName()); 171 exportedPackageInterfaces->push_back(rootTypeName); 172 } 173 return OK; 174 } 175 176 status_t OutputHandler::appendTargets(const FQName& fqName, const Coordinator* coordinator, 177 std::vector<FQName>* targets) const { 178 switch (mGenerationGranularity) { 179 case GenerationGranularity::PER_PACKAGE: { 180 targets->push_back(fqName.getPackageAndVersion()); 181 } break; 182 case GenerationGranularity::PER_FILE: { 183 if (fqName.isFullyQualified()) { 184 targets->push_back(fqName); 185 break; 186 } 187 status_t err = coordinator->appendPackageInterfacesToVector(fqName, targets); 188 if (err != OK) return err; 189 } break; 190 case GenerationGranularity::PER_TYPE: { 191 if (fqName.isFullyQualified()) { 192 status_t err = appendPerTypeTargets(fqName, coordinator, targets); 193 if (err != OK) return err; 194 break; 195 } 196 197 std::vector<FQName> packageInterfaces; 198 status_t err = coordinator->appendPackageInterfacesToVector(fqName, &packageInterfaces); 199 if (err != OK) return err; 200 for (const FQName& packageInterface : packageInterfaces) { 201 err = appendPerTypeTargets(packageInterface, coordinator, targets); 202 if (err != OK) return err; 203 } 204 } break; 205 default: 206 CHECK(!"Should be here"); 207 } 208 209 return OK; 210 } 211 212 status_t OutputHandler::generate(const FQName& fqName, const Coordinator* coordinator) const { 213 std::vector<FQName> targets; 214 status_t err = appendTargets(fqName, coordinator, &targets); 215 if (err != OK) return err; 216 217 for (const FQName& fqName : targets) { 218 for (const FileGenerator& file : mGenerateFunctions) { 219 status_t err = file.generate(fqName, coordinator, mLocation); 220 if (err != OK) return err; 221 } 222 } 223 224 return OK; 225 } 226 227 status_t OutputHandler::appendOutputFiles(const FQName& fqName, const Coordinator* coordinator, 228 std::vector<std::string>* outputFiles) const { 229 std::vector<FQName> targets; 230 status_t err = appendTargets(fqName, coordinator, &targets); 231 if (err != OK) return err; 232 233 for (const FQName& fqName : targets) { 234 for (const FileGenerator& file : mGenerateFunctions) { 235 err = file.appendOutputFiles(fqName, coordinator, mLocation, outputFiles); 236 if (err != OK) return err; 237 } 238 } 239 240 return OK; 241 } 242 243 status_t OutputHandler::writeDepFile(const FQName& fqName, const Coordinator* coordinator) const { 244 std::vector<std::string> outputFiles; 245 status_t err = appendOutputFiles(fqName, coordinator, &outputFiles); 246 if (err != OK) return err; 247 248 // No need for dep files 249 if (outputFiles.empty()) { 250 return OK; 251 } 252 253 // Depfiles in Android for genrules should be for the 'main file'. Because hidl-gen doesn't have 254 // a main file for most targets, we are just outputting a depfile for one single file only. 255 const std::string forFile = outputFiles[0]; 256 257 return coordinator->writeDepFile(forFile); 258 } 259 260 // Use an AST function as a OutputHandler GenerationFunction 261 static FileGenerator::GenerationFunction astGenerationFunction(void (AST::*generate)(Formatter&) 262 const = nullptr) { 263 return [generate](const FQName& fqName, const Coordinator* coordinator, 264 const FileGenerator::GetFormatter& getFormatter) -> status_t { 265 AST* ast = coordinator->parse(fqName); 266 if (ast == nullptr) { 267 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); 268 return UNKNOWN_ERROR; 269 } 270 271 if (generate == nullptr) return OK; // just parsing AST 272 273 Formatter out = getFormatter(); 274 if (!out.isValid()) { 275 return UNKNOWN_ERROR; 276 } 277 278 (ast->*generate)(out); 279 280 return OK; 281 }; 282 } 283 284 // Common pattern: single file for package or standard out 285 static FileGenerator singleFileGenerator( 286 const std::string& fileName, const FileGenerator::GenerationFunction& generationFunction) { 287 return { 288 FileGenerator::alwaysGenerate, [fileName](const FQName&) { return fileName; }, 289 generationFunction, 290 }; 291 } 292 293 static status_t generateJavaForPackage(const FQName& fqName, const Coordinator* coordinator, 294 const FileGenerator::GetFormatter& getFormatter) { 295 AST* ast; 296 std::string limitToType; 297 FQName typeName; 298 299 // See appendPerTypeTargets. 300 // 'a.b.c@1.0::types.Foo' is used to compile 'Foo' for Java even though in 301 // the rest of the compiler, this type is simply called 'a.b.c@1.0::Foo'. 302 // However, here, we need to disambiguate an interface name and a type in 303 // types.hal in order to figure out what to parse, so this legacy behavior 304 // is kept. 305 if (fqName.name().find("types.") == 0) { 306 limitToType = fqName.name().substr(strlen("types.")); 307 308 ast = coordinator->parse(fqName.getTypesForPackage()); 309 310 const auto& names = fqName.names(); 311 CHECK(names.size() == 2 && names[0] == "types") << fqName.string(); 312 typeName = FQName(fqName.package(), fqName.version(), names[1]); 313 } else { 314 ast = coordinator->parse(fqName); 315 typeName = fqName; 316 } 317 if (ast == nullptr) { 318 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); 319 return UNKNOWN_ERROR; 320 } 321 322 Type* type = ast->lookupType(typeName, &ast->getRootScope()); 323 CHECK(type != nullptr) << typeName.string(); 324 if (!type->isJavaCompatible()) { 325 return OK; 326 } 327 328 Formatter out = getFormatter(); 329 if (!out.isValid()) { 330 return UNKNOWN_ERROR; 331 } 332 333 ast->generateJava(out, limitToType); 334 return OK; 335 }; 336 337 static status_t dumpDefinedButUnreferencedTypeNames(const FQName& packageFQName, 338 const Coordinator* coordinator) { 339 std::vector<FQName> packageInterfaces; 340 status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces); 341 if (err != OK) return err; 342 343 std::set<FQName> unreferencedDefinitions; 344 std::set<FQName> unreferencedImports; 345 err = coordinator->addUnreferencedTypes(packageInterfaces, &unreferencedDefinitions, 346 &unreferencedImports); 347 if (err != OK) return err; 348 349 for (const auto& fqName : unreferencedDefinitions) { 350 std::cerr 351 << "VERBOSE: DEFINED-BUT-NOT-REFERENCED " 352 << fqName.string() 353 << std::endl; 354 } 355 356 for (const auto& fqName : unreferencedImports) { 357 std::cerr 358 << "VERBOSE: IMPORTED-BUT-NOT-REFERENCED " 359 << fqName.string() 360 << std::endl; 361 } 362 363 return OK; 364 } 365 366 static std::string makeLibraryName(const FQName &packageFQName) { 367 return packageFQName.string(); 368 } 369 370 static status_t isPackageJavaCompatible(const FQName& packageFQName, const Coordinator* coordinator, 371 bool* compatible) { 372 std::vector<FQName> todo; 373 status_t err = 374 coordinator->appendPackageInterfacesToVector(packageFQName, &todo); 375 376 if (err != OK) { 377 return err; 378 } 379 380 std::set<FQName> seen; 381 for (const auto &iface : todo) { 382 seen.insert(iface); 383 } 384 385 // Form the transitive closure of all imported interfaces (and types.hal-s) 386 // If any one of them is not java compatible, this package isn't either. 387 while (!todo.empty()) { 388 const FQName fqName = todo.back(); 389 todo.pop_back(); 390 391 AST *ast = coordinator->parse(fqName); 392 393 if (ast == nullptr) { 394 return UNKNOWN_ERROR; 395 } 396 397 if (!ast->isJavaCompatible()) { 398 *compatible = false; 399 return OK; 400 } 401 402 std::set<FQName> importedPackages; 403 ast->getImportedPackages(&importedPackages); 404 405 for (const auto &package : importedPackages) { 406 std::vector<FQName> packageInterfaces; 407 status_t err = coordinator->appendPackageInterfacesToVector( 408 package, &packageInterfaces); 409 410 if (err != OK) { 411 return err; 412 } 413 414 for (const auto &iface : packageInterfaces) { 415 if (seen.find(iface) != seen.end()) { 416 continue; 417 } 418 419 todo.push_back(iface); 420 seen.insert(iface); 421 } 422 } 423 } 424 425 *compatible = true; 426 return OK; 427 } 428 429 static bool packageNeedsJavaCode( 430 const std::vector<FQName> &packageInterfaces, AST *typesAST) { 431 if (packageInterfaces.size() == 0) { 432 return false; 433 } 434 435 // If there is more than just a types.hal file to this package we'll 436 // definitely need to generate Java code. 437 if (packageInterfaces.size() > 1 438 || packageInterfaces[0].name() != "types") { 439 return true; 440 } 441 442 CHECK(typesAST != nullptr); 443 444 // We'll have to generate Java code if types.hal contains any non-typedef 445 // type declarations. 446 447 std::vector<NamedType*> subTypes = typesAST->getRootScope().getSubTypes(); 448 for (const auto &subType : subTypes) { 449 if (!subType->isTypeDef()) { 450 return true; 451 } 452 } 453 454 return false; 455 } 456 457 bool validateIsPackage(const FQName& fqName, const Coordinator*, 458 const std::string& /* language */) { 459 if (fqName.package().empty()) { 460 fprintf(stderr, "ERROR: Expecting package name\n"); 461 return false; 462 } 463 464 if (fqName.version().empty()) { 465 fprintf(stderr, "ERROR: Expecting package version\n"); 466 return false; 467 } 468 469 if (!fqName.name().empty()) { 470 fprintf(stderr, 471 "ERROR: Expecting only package name and version.\n"); 472 return false; 473 } 474 475 return true; 476 } 477 478 bool isHidlTransportPackage(const FQName& fqName) { 479 return fqName.package() == gIBaseFqName.package() || 480 fqName.package() == gIManagerFqName.package(); 481 } 482 483 bool isSystemProcessSupportedPackage(const FQName& fqName) { 484 // Technically, so is hidl IBase + IServiceManager, but 485 // these are part of libhidlbase. 486 return fqName.inPackage("android.hardware.graphics.common") || 487 fqName.inPackage("android.hardware.graphics.mapper") || 488 fqName.string() == "android.hardware.renderscript@1.0" || 489 fqName.string() == "android.hidl.memory.token@1.0" || 490 fqName.string() == "android.hidl.memory@1.0" || 491 fqName.string() == "android.hidl.safe_union@1.0"; 492 } 493 494 bool isCoreAndroidPackage(const FQName& package) { 495 return package.inPackage("android.hidl") || 496 package.inPackage("android.system") || 497 package.inPackage("android.frameworks") || 498 package.inPackage("android.hardware"); 499 } 500 501 // TODO(b/69862859): remove special case 502 status_t isTestPackage(const FQName& fqName, const Coordinator* coordinator, bool* isTestPackage) { 503 const auto fileExists = [](const std::string& file) { 504 struct stat buf; 505 return stat(file.c_str(), &buf) == 0; 506 }; 507 508 std::string path; 509 status_t err = coordinator->getFilepath(fqName, Coordinator::Location::PACKAGE_ROOT, 510 ".hidl_for_test", &path); 511 if (err != OK) return err; 512 513 const bool exists = fileExists(path); 514 515 if (exists) { 516 coordinator->onFileAccess(path, "r"); 517 } 518 519 *isTestPackage = exists; 520 return OK; 521 } 522 523 static status_t generateAdapterMainSource(const FQName& packageFQName, 524 const Coordinator* coordinator, 525 const FileGenerator::GetFormatter& getFormatter) { 526 std::vector<FQName> packageInterfaces; 527 status_t err = 528 coordinator->appendPackageInterfacesToVector(packageFQName, 529 &packageInterfaces); 530 if (err != OK) { 531 return err; 532 } 533 534 // b/146223994: parse all interfaces 535 // - causes files to get read (filling out dep file) 536 // - avoid creating successful output for broken files 537 for (const FQName& fqName : packageInterfaces) { 538 AST* ast = coordinator->parse(fqName); 539 if (ast == nullptr) { 540 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); 541 return UNKNOWN_ERROR; 542 } 543 } 544 545 Formatter out = getFormatter(); 546 if (!out.isValid()) { 547 return UNKNOWN_ERROR; 548 } 549 550 out << "#include <hidladapter/HidlBinderAdapter.h>\n"; 551 552 for (auto &interface : packageInterfaces) { 553 if (interface.name() == "types") { 554 continue; 555 } 556 AST::generateCppPackageInclude(out, interface, interface.getInterfaceAdapterName()); 557 } 558 559 out << "int main(int argc, char** argv) "; 560 out.block([&] { 561 out << "return ::android::hardware::adapterMain<\n"; 562 out.indent(); 563 for (auto &interface : packageInterfaces) { 564 if (interface.name() == "types") { 565 continue; 566 } 567 out << interface.getInterfaceAdapterFqName().cppName(); 568 569 if (&interface != &packageInterfaces.back()) { 570 out << ",\n"; 571 } 572 } 573 out << ">(\"" << packageFQName.string() << "\", argc, argv);\n"; 574 out.unindent(); 575 }).endl(); 576 return OK; 577 } 578 579 static status_t generateAndroidBpForPackage(const FQName& packageFQName, 580 const Coordinator* coordinator, 581 const FileGenerator::GetFormatter& getFormatter) { 582 CHECK(!packageFQName.isFullyQualified() && packageFQName.name().empty()); 583 584 std::vector<FQName> packageInterfaces; 585 586 status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces); 587 588 if (err != OK) { 589 return err; 590 } 591 592 std::set<FQName> importedPackagesHierarchy; 593 std::vector<const Type *> exportedTypes; 594 AST* typesAST = nullptr; 595 596 for (const auto& fqName : packageInterfaces) { 597 AST* ast = coordinator->parse(fqName); 598 599 if (ast == nullptr) { 600 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); 601 602 return UNKNOWN_ERROR; 603 } 604 605 if (fqName.name() == "types") { 606 typesAST = ast; 607 } 608 609 ast->getImportedPackagesHierarchy(&importedPackagesHierarchy); 610 ast->appendToExportedTypesVector(&exportedTypes); 611 } 612 613 bool needsJavaCode = packageNeedsJavaCode(packageInterfaces, typesAST); 614 615 bool genJavaConstants = needsJavaCode && !exportedTypes.empty(); 616 617 bool isJavaCompatible; 618 err = isPackageJavaCompatible(packageFQName, coordinator, &isJavaCompatible); 619 if (err != OK) return err; 620 bool genJavaLibrary = needsJavaCode && isJavaCompatible; 621 622 bool generateForTest; 623 err = isTestPackage(packageFQName, coordinator, &generateForTest); 624 if (err != OK) return err; 625 626 bool isCoreAndroid = isCoreAndroidPackage(packageFQName); 627 628 bool isVndk = !generateForTest && isCoreAndroid; 629 bool isVndkSp = isVndk && isSystemProcessSupportedPackage(packageFQName); 630 631 // Currently, all platform-provided interfaces are in the VNDK, so if it isn't in the VNDK, it 632 // is device specific and so should be put in the system_ext partition. 633 bool isSystemExt = !isCoreAndroid; 634 635 std::string packageRoot; 636 err = coordinator->getPackageRoot(packageFQName, &packageRoot); 637 if (err != OK) return err; 638 639 Formatter out = getFormatter(); 640 if (!out.isValid()) { 641 return UNKNOWN_ERROR; 642 } 643 644 out << "// This file is autogenerated by hidl-gen -Landroidbp.\n\n"; 645 646 out << "hidl_interface "; 647 out.block([&] { 648 out << "name: \"" << makeLibraryName(packageFQName) << "\",\n"; 649 if (!coordinator->getOwner().empty()) { 650 out << "owner: \"" << coordinator->getOwner() << "\",\n"; 651 } 652 out << "root: \"" << packageRoot << "\",\n"; 653 if (isVndk) { 654 out << "vndk: "; 655 out.block([&]() { 656 out << "enabled: true,\n"; 657 if (isVndkSp) { 658 out << "support_system_process: true,\n"; 659 } 660 }) << ",\n"; 661 } 662 if (isSystemExt) { 663 out << "system_ext_specific: true,\n"; 664 } 665 (out << "srcs: [\n").indent([&] { 666 for (const auto& fqName : packageInterfaces) { 667 out << "\"" << fqName.name() << ".hal\",\n"; 668 } 669 }) << "],\n"; 670 if (!importedPackagesHierarchy.empty()) { 671 (out << "interfaces: [\n").indent([&] { 672 for (const auto& fqName : importedPackagesHierarchy) { 673 out << "\"" << fqName.string() << "\",\n"; 674 } 675 }) << "],\n"; 676 } 677 // Explicity call this out for developers. 678 out << "gen_java: " << (genJavaLibrary ? "true" : "false") << ",\n"; 679 if (genJavaConstants) { 680 out << "gen_java_constants: true,\n"; 681 } 682 }).endl(); 683 684 return OK; 685 } 686 687 static status_t generateAndroidBpImplForPackage(const FQName& packageFQName, 688 const Coordinator* coordinator, 689 const FileGenerator::GetFormatter& getFormatter) { 690 const std::string libraryName = makeLibraryName(packageFQName) + "-impl"; 691 692 std::vector<FQName> packageInterfaces; 693 694 status_t err = 695 coordinator->appendPackageInterfacesToVector(packageFQName, 696 &packageInterfaces); 697 698 if (err != OK) { 699 return err; 700 } 701 702 std::set<FQName> importedPackages; 703 704 for (const auto &fqName : packageInterfaces) { 705 AST *ast = coordinator->parse(fqName); 706 707 if (ast == nullptr) { 708 fprintf(stderr, 709 "ERROR: Could not parse %s. Aborting.\n", 710 fqName.string().c_str()); 711 712 return UNKNOWN_ERROR; 713 } 714 715 ast->getImportedPackages(&importedPackages); 716 } 717 718 Formatter out = getFormatter(); 719 if (!out.isValid()) { 720 return UNKNOWN_ERROR; 721 } 722 723 out << "// FIXME: your file license if you have one\n\n"; 724 out << "cc_library_shared {\n"; 725 out.indent([&] { 726 out << "// FIXME: this should only be -impl for a passthrough hal.\n" 727 << "// In most cases, to convert this to a binderized implementation, you should:\n" 728 << "// - change '-impl' to '-service' here and make it a cc_binary instead of a\n" 729 << "// cc_library_shared.\n" 730 << "// - add a *.rc file for this module.\n" 731 << "// - delete HIDL_FETCH_I* functions.\n" 732 << "// - call configureRpcThreadpool and registerAsService on the instance.\n" 733 << "// You may also want to append '-impl/-service' with a specific identifier like\n" 734 << "// '-vendor' or '-<hardware identifier>' etc to distinguish it.\n"; 735 out << "name: \"" << libraryName << "\",\n"; 736 if (!coordinator->getOwner().empty()) { 737 out << "owner: \"" << coordinator->getOwner() << "\",\n"; 738 } 739 out << "relative_install_path: \"hw\",\n"; 740 if (coordinator->getOwner().empty()) { 741 out << "// FIXME: this should be 'vendor: true' for modules that will eventually be\n" 742 "// on AOSP.\n"; 743 } 744 out << "proprietary: true,\n"; 745 out << "srcs: [\n"; 746 out.indent([&] { 747 for (const auto &fqName : packageInterfaces) { 748 if (fqName.name() == "types") { 749 continue; 750 } 751 out << "\"" << fqName.getInterfaceBaseName() << ".cpp\",\n"; 752 } 753 }); 754 out << "],\n" 755 << "shared_libs: [\n"; 756 out.indent([&] { 757 out << "\"libhidlbase\",\n" 758 << "\"libutils\",\n" 759 << "\"" << makeLibraryName(packageFQName) << "\",\n"; 760 761 for (const auto &importedPackage : importedPackages) { 762 if (isHidlTransportPackage(importedPackage)) { 763 continue; 764 } 765 766 out << "\"" << makeLibraryName(importedPackage) << "\",\n"; 767 } 768 }); 769 out << "],\n"; 770 }); 771 out << "}\n"; 772 773 return OK; 774 } 775 776 bool validateForSource(const FQName& fqName, const Coordinator* coordinator, 777 const std::string& language) { 778 if (fqName.package().empty()) { 779 fprintf(stderr, "ERROR: Expecting package name\n"); 780 return false; 781 } 782 783 if (fqName.version().empty()) { 784 fprintf(stderr, "ERROR: Expecting package version\n"); 785 return false; 786 } 787 788 const std::string &name = fqName.name(); 789 if (!name.empty()) { 790 if (name.find('.') == std::string::npos) { 791 return true; 792 } 793 794 if (language != "java" || name.find("types.") != 0) { 795 // When generating java sources for "types.hal", output can be 796 // constrained to just one of the top-level types declared 797 // by using the extended syntax 798 // android.hardware.Foo@1.0::types.TopLevelTypeName. 799 // In all other cases (different language, not 'types') the dot 800 // notation in the name is illegal in this context. 801 return false; 802 } 803 804 return true; 805 } 806 807 if (language == "java") { 808 bool isJavaCompatible; 809 status_t err = isPackageJavaCompatible(fqName, coordinator, &isJavaCompatible); 810 if (err != OK) return false; 811 812 if (!isJavaCompatible) { 813 fprintf(stderr, 814 "ERROR: %s is not Java compatible. The Java backend does NOT support union " 815 "types. In addition, vectors of arrays are limited to at most one-dimensional " 816 "arrays and vectors of {vectors,interfaces,memory} are not supported.\n", 817 fqName.string().c_str()); 818 return false; 819 } 820 } 821 822 return true; 823 } 824 825 FileGenerator::GenerationFunction generateExportHeaderForPackage(bool forJava) { 826 return [forJava](const FQName& packageFQName, const Coordinator* coordinator, 827 const FileGenerator::GetFormatter& getFormatter) -> status_t { 828 CHECK(!packageFQName.package().empty() && !packageFQName.version().empty() && 829 packageFQName.name().empty()); 830 831 std::vector<FQName> packageInterfaces; 832 833 status_t err = coordinator->appendPackageInterfacesToVector( 834 packageFQName, &packageInterfaces); 835 836 if (err != OK) { 837 return err; 838 } 839 840 std::vector<const Type *> exportedTypes; 841 842 for (const auto &fqName : packageInterfaces) { 843 AST *ast = coordinator->parse(fqName); 844 845 if (ast == nullptr) { 846 fprintf(stderr, 847 "ERROR: Could not parse %s. Aborting.\n", 848 fqName.string().c_str()); 849 850 return UNKNOWN_ERROR; 851 } 852 853 ast->appendToExportedTypesVector(&exportedTypes); 854 } 855 856 if (exportedTypes.empty()) { 857 return OK; 858 } 859 860 Formatter out = getFormatter(); 861 if (!out.isValid()) { 862 return UNKNOWN_ERROR; 863 } 864 865 std::string packagePath; 866 err = coordinator->getPackagePath(packageFQName, false /* relative */, 867 false /* sanitized */, &packagePath); 868 if (err != OK) return err; 869 870 out << "// This file is autogenerated by hidl-gen. Do not edit manually.\n" 871 << "// Source: " << packageFQName.string() << "\n" 872 << "// Location: " << packagePath << "\n\n"; 873 874 std::string guard; 875 if (forJava) { 876 out << "package " << packageFQName.javaPackage() << ";\n\n"; 877 out << "public class Constants {\n"; 878 out.indent(); 879 } else { 880 guard = "HIDL_GENERATED_"; 881 guard += StringHelper::Uppercase(packageFQName.tokenName()); 882 guard += "_"; 883 guard += "EXPORTED_CONSTANTS_H_"; 884 885 out << "#ifndef " 886 << guard 887 << "\n#define " 888 << guard 889 << "\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"; 890 } 891 892 for (const auto &type : exportedTypes) { 893 type->emitExportedHeader(out, forJava); 894 } 895 896 if (forJava) { 897 out.unindent(); 898 out << "}\n"; 899 } else { 900 out << "#ifdef __cplusplus\n}\n#endif\n\n#endif // " 901 << guard 902 << "\n"; 903 } 904 905 return OK; 906 }; 907 } 908 909 static status_t generateHashOutput(const FQName& fqName, const Coordinator* coordinator, 910 const FileGenerator::GetFormatter& getFormatter) { 911 CHECK(fqName.isFullyQualified()); 912 913 AST* ast = coordinator->parse(fqName, {} /* parsed */, 914 Coordinator::Enforce::NO_HASH /* enforcement */); 915 916 if (ast == nullptr) { 917 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); 918 919 return UNKNOWN_ERROR; 920 } 921 922 Formatter out = getFormatter(); 923 if (!out.isValid()) { 924 return UNKNOWN_ERROR; 925 } 926 927 out << Hash::getHash(ast->getFilename()).hexString() << " " << fqName.string() << "\n"; 928 929 return OK; 930 } 931 932 static status_t generateFunctionCount(const FQName& fqName, const Coordinator* coordinator, 933 const FileGenerator::GetFormatter& getFormatter) { 934 CHECK(fqName.isFullyQualified()); 935 936 AST* ast = coordinator->parse(fqName, {} /* parsed */, 937 Coordinator::Enforce::NO_HASH /* enforcement */); 938 939 if (ast == nullptr) { 940 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); 941 return UNKNOWN_ERROR; 942 } 943 944 const Interface* interface = ast->getInterface(); 945 if (interface == nullptr) { 946 fprintf(stderr, "ERROR: Function count requires interface: %s.\n", fqName.string().c_str()); 947 return UNKNOWN_ERROR; 948 } 949 950 Formatter out = getFormatter(); 951 if (!out.isValid()) { 952 return UNKNOWN_ERROR; 953 } 954 955 // This is wrong for android.hidl.base@1.0::IBase, but in that case, it doesn't matter. 956 // This is just the number of APIs that are added. 957 out << fqName.string() << " " << interface->userDefinedMethods().size() << "\n"; 958 959 return OK; 960 } 961 962 template <typename T> 963 std::vector<T> operator+(const std::vector<T>& lhs, const std::vector<T>& rhs) { 964 std::vector<T> ret; 965 ret.reserve(lhs.size() + rhs.size()); 966 ret.insert(ret.begin(), lhs.begin(), lhs.end()); 967 ret.insert(ret.end(), rhs.begin(), rhs.end()); 968 return ret; 969 } 970 971 // clang-format off 972 static const std::vector<FileGenerator> kCppHeaderFormats = { 973 { 974 FileGenerator::alwaysGenerate, 975 [](const FQName& fqName) { return fqName.name() + ".h"; }, 976 astGenerationFunction(&AST::generateInterfaceHeader), 977 }, 978 { 979 FileGenerator::alwaysGenerate, 980 [](const FQName& fqName) { 981 return fqName.isInterfaceName() ? fqName.getInterfaceHwName() + ".h" : "hwtypes.h"; 982 }, 983 astGenerationFunction(&AST::generateHwBinderHeader), 984 }, 985 { 986 FileGenerator::generateForInterfaces, 987 [](const FQName& fqName) { return fqName.getInterfaceStubName() + ".h"; }, 988 astGenerationFunction(&AST::generateStubHeader), 989 }, 990 { 991 FileGenerator::generateForInterfaces, 992 [](const FQName& fqName) { return fqName.getInterfaceProxyName() + ".h"; }, 993 astGenerationFunction(&AST::generateProxyHeader), 994 }, 995 { 996 FileGenerator::generateForInterfaces, 997 [](const FQName& fqName) { return fqName.getInterfacePassthroughName() + ".h"; }, 998 astGenerationFunction(&AST::generatePassthroughHeader), 999 }, 1000 }; 1001 1002 static const std::vector<FileGenerator> kCppSourceFormats = { 1003 { 1004 FileGenerator::alwaysGenerate, 1005 [](const FQName& fqName) { 1006 return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + "All.cpp" : "types.cpp"; 1007 }, 1008 astGenerationFunction(&AST::generateCppSource), 1009 }, 1010 }; 1011 1012 static const std::vector<FileGenerator> kCppImplHeaderFormats = { 1013 { 1014 FileGenerator::generateForInterfaces, 1015 [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".h"; }, 1016 astGenerationFunction(&AST::generateCppImplHeader), 1017 }, 1018 }; 1019 1020 static const std::vector<FileGenerator> kCppImplSourceFormats = { 1021 { 1022 FileGenerator::generateForInterfaces, 1023 [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".cpp"; }, 1024 astGenerationFunction(&AST::generateCppImplSource), 1025 }, 1026 }; 1027 1028 static const std::vector<FileGenerator> kCppAdapterHeaderFormats = { 1029 { 1030 FileGenerator::alwaysGenerate, 1031 [](const FQName& fqName) { 1032 return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".h" : "Atypes.h"; 1033 }, 1034 astGenerationFunction(&AST::generateCppAdapterHeader), 1035 }, 1036 }; 1037 1038 static const std::vector<FileGenerator> kCppAdapterSourceFormats = { 1039 { 1040 FileGenerator::alwaysGenerate, 1041 [](const FQName& fqName) { 1042 return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".cpp" : "Atypes.cpp"; 1043 }, 1044 astGenerationFunction(&AST::generateCppAdapterSource), 1045 }, 1046 }; 1047 1048 static const std::vector<OutputHandler> kFormats = { 1049 { 1050 "check", 1051 "Parses the interface to see if valid but doesn't write any files.", 1052 OutputMode::NOT_NEEDED, 1053 Coordinator::Location::STANDARD_OUT, 1054 GenerationGranularity::PER_FILE, 1055 validateForSource, 1056 { 1057 { 1058 FileGenerator::alwaysGenerate, 1059 nullptr /* filename for fqname */, 1060 astGenerationFunction(), 1061 }, 1062 }, 1063 }, 1064 { 1065 "c++", 1066 "(internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.", 1067 OutputMode::NEEDS_DIR, 1068 Coordinator::Location::GEN_OUTPUT, 1069 GenerationGranularity::PER_FILE, 1070 validateForSource, 1071 kCppHeaderFormats + kCppSourceFormats, 1072 }, 1073 { 1074 "c++-headers", 1075 "(internal) Generates C++ headers for interface files for talking to HIDL interfaces.", 1076 OutputMode::NEEDS_DIR, 1077 Coordinator::Location::GEN_OUTPUT, 1078 GenerationGranularity::PER_FILE, 1079 validateForSource, 1080 kCppHeaderFormats, 1081 }, 1082 { 1083 "c++-sources", 1084 "(internal) Generates C++ sources for interface files for talking to HIDL interfaces.", 1085 OutputMode::NEEDS_DIR, 1086 Coordinator::Location::GEN_OUTPUT, 1087 GenerationGranularity::PER_FILE, 1088 validateForSource, 1089 kCppSourceFormats, 1090 }, 1091 { 1092 "export-header", 1093 "Generates a header file from @export enumerations to help maintain legacy code.", 1094 OutputMode::NEEDS_FILE, 1095 Coordinator::Location::DIRECT, 1096 GenerationGranularity::PER_PACKAGE, 1097 validateIsPackage, 1098 {singleFileGenerator("", generateExportHeaderForPackage(false /* forJava */))} 1099 }, 1100 { 1101 "c++-impl", 1102 "Generates boilerplate implementation of a hidl interface in C++ (for convenience).", 1103 OutputMode::NEEDS_DIR, 1104 Coordinator::Location::DIRECT, 1105 GenerationGranularity::PER_FILE, 1106 validateForSource, 1107 kCppImplHeaderFormats + kCppImplSourceFormats, 1108 }, 1109 { 1110 "c++-impl-headers", 1111 "c++-impl but headers only.", 1112 OutputMode::NEEDS_DIR, 1113 Coordinator::Location::DIRECT, 1114 GenerationGranularity::PER_FILE, 1115 validateForSource, 1116 kCppImplHeaderFormats, 1117 }, 1118 { 1119 "c++-impl-sources", 1120 "c++-impl but sources only.", 1121 OutputMode::NEEDS_DIR, 1122 Coordinator::Location::DIRECT, 1123 GenerationGranularity::PER_FILE, 1124 validateForSource, 1125 kCppImplSourceFormats, 1126 }, 1127 { 1128 "c++-adapter", 1129 "Takes a x.(y+n) interface and mocks an x.y interface.", 1130 OutputMode::NEEDS_DIR, 1131 Coordinator::Location::GEN_OUTPUT, 1132 GenerationGranularity::PER_FILE, 1133 validateForSource, 1134 kCppAdapterHeaderFormats + kCppAdapterSourceFormats, 1135 }, 1136 { 1137 "c++-adapter-headers", 1138 "c++-adapter but helper headers only.", 1139 OutputMode::NEEDS_DIR, 1140 Coordinator::Location::GEN_OUTPUT, 1141 GenerationGranularity::PER_FILE, 1142 validateForSource, 1143 kCppAdapterHeaderFormats, 1144 }, 1145 { 1146 "c++-adapter-sources", 1147 "c++-adapter but helper sources only.", 1148 OutputMode::NEEDS_DIR, 1149 Coordinator::Location::GEN_OUTPUT, 1150 GenerationGranularity::PER_FILE, 1151 validateForSource, 1152 kCppAdapterSourceFormats, 1153 }, 1154 { 1155 "c++-adapter-main", 1156 "c++-adapter but the adapter binary source only.", 1157 OutputMode::NEEDS_DIR, 1158 Coordinator::Location::DIRECT, 1159 GenerationGranularity::PER_PACKAGE, 1160 validateIsPackage, 1161 {singleFileGenerator("main.cpp", generateAdapterMainSource)}, 1162 }, 1163 { 1164 "java", 1165 "(internal) Generates Java library for talking to HIDL interfaces in Java.", 1166 OutputMode::NEEDS_DIR, 1167 Coordinator::Location::GEN_SANITIZED, 1168 GenerationGranularity::PER_TYPE, 1169 validateForSource, 1170 { 1171 { 1172 FileGenerator::alwaysGenerate, 1173 [](const FQName& fqName) { 1174 return StringHelper::LTrim(fqName.name(), "types.") + ".java"; 1175 }, 1176 generateJavaForPackage, 1177 }, 1178 } 1179 }, 1180 { 1181 "java-impl", 1182 "Generates boilerplate implementation of a hidl interface in Java (for convenience).", 1183 OutputMode::NEEDS_DIR, 1184 Coordinator::Location::DIRECT, 1185 GenerationGranularity::PER_FILE, 1186 validateForSource, 1187 { 1188 { 1189 FileGenerator::generateForInterfaces, 1190 [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".java"; }, 1191 astGenerationFunction(&AST::generateJavaImpl), 1192 }, 1193 } 1194 }, 1195 { 1196 "java-constants", 1197 "(internal) Like export-header but for Java (always created by -Lmakefile if @export exists).", 1198 OutputMode::NEEDS_DIR, 1199 Coordinator::Location::GEN_SANITIZED, 1200 GenerationGranularity::PER_PACKAGE, 1201 validateIsPackage, 1202 {singleFileGenerator("Constants.java", generateExportHeaderForPackage(true /* forJava */))} 1203 }, 1204 { 1205 "vts", 1206 "(internal) Generates vts proto files for use in vtsd.", 1207 OutputMode::NEEDS_DIR, 1208 Coordinator::Location::GEN_OUTPUT, 1209 GenerationGranularity::PER_FILE, 1210 validateForSource, 1211 { 1212 { 1213 FileGenerator::alwaysGenerate, 1214 [](const FQName& fqName) { 1215 return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + ".vts" : "types.vts"; 1216 }, 1217 astGenerationFunction(&AST::generateVts), 1218 }, 1219 } 1220 }, 1221 { 1222 "makefile", 1223 "(removed) Used to generate makefiles for -Ljava and -Ljava-constants.", 1224 OutputMode::NEEDS_SRC, 1225 Coordinator::Location::PACKAGE_ROOT, 1226 GenerationGranularity::PER_PACKAGE, 1227 [](const FQName &, const Coordinator*, const std::string &) { 1228 fprintf(stderr, "ERROR: makefile output is not supported. Use -Landroidbp for all build file generation.\n"); 1229 return false; 1230 }, 1231 {}, 1232 }, 1233 { 1234 "androidbp", 1235 "(internal) Generates Soong bp files for -Lc++-headers, -Lc++-sources, -Ljava, -Ljava-constants, and -Lc++-adapter.", 1236 OutputMode::NEEDS_SRC, 1237 Coordinator::Location::PACKAGE_ROOT, 1238 GenerationGranularity::PER_PACKAGE, 1239 validateIsPackage, 1240 {singleFileGenerator("Android.bp", generateAndroidBpForPackage)}, 1241 }, 1242 { 1243 "androidbp-impl", 1244 "Generates boilerplate bp files for implementation created with -Lc++-impl.", 1245 OutputMode::NEEDS_DIR, 1246 Coordinator::Location::DIRECT, 1247 GenerationGranularity::PER_PACKAGE, 1248 validateIsPackage, 1249 {singleFileGenerator("Android.bp", generateAndroidBpImplForPackage)}, 1250 }, 1251 { 1252 "hash", 1253 "Prints hashes of interface in `current.txt` format to standard out.", 1254 OutputMode::NOT_NEEDED, 1255 Coordinator::Location::STANDARD_OUT, 1256 GenerationGranularity::PER_FILE, 1257 validateForSource, 1258 { 1259 { 1260 FileGenerator::alwaysGenerate, 1261 nullptr /* file name for fqName */, 1262 generateHashOutput, 1263 }, 1264 } 1265 }, 1266 { 1267 "function-count", 1268 "Prints the total number of functions added by the package or interface.", 1269 OutputMode::NOT_NEEDED, 1270 Coordinator::Location::STANDARD_OUT, 1271 GenerationGranularity::PER_FILE, 1272 validateForSource, 1273 { 1274 { 1275 FileGenerator::generateForInterfaces, 1276 nullptr /* file name for fqName */, 1277 generateFunctionCount, 1278 }, 1279 } 1280 }, 1281 { 1282 "dependencies", 1283 "Prints all depended types.", 1284 OutputMode::NOT_NEEDED, 1285 Coordinator::Location::STANDARD_OUT, 1286 GenerationGranularity::PER_FILE, 1287 validateForSource, 1288 { 1289 { 1290 FileGenerator::alwaysGenerate, 1291 nullptr /* file name for fqName */, 1292 astGenerationFunction(&AST::generateDependencies), 1293 }, 1294 }, 1295 }, 1296 { 1297 "inheritance-hierarchy", 1298 "Prints the hierarchy of inherited types as a JSON object.", 1299 OutputMode::NOT_NEEDED, 1300 Coordinator::Location::STANDARD_OUT, 1301 GenerationGranularity::PER_FILE, 1302 validateForSource, 1303 { 1304 { 1305 FileGenerator::alwaysGenerate, 1306 nullptr /* file name for fqName */, 1307 astGenerationFunction(&AST::generateInheritanceHierarchy), 1308 }, 1309 }, 1310 }, 1311 { 1312 "format", 1313 "Reformats the .hal files", 1314 OutputMode::NEEDS_SRC, 1315 Coordinator::Location::PACKAGE_ROOT, 1316 GenerationGranularity::PER_FILE, 1317 validateForSource, 1318 { 1319 { 1320 FileGenerator::alwaysGenerate, 1321 [](const FQName& fqName) { return fqName.name() + ".hal"; }, 1322 astGenerationFunction(&AST::generateFormattedHidl), 1323 }, 1324 } 1325 }, 1326 }; 1327 // clang-format on 1328 1329 static void usage(const char* me) { 1330 Formatter out(stderr); 1331 1332 out << "Usage: " << me << " -o <output path> -L <language> [-O <owner>] "; 1333 Coordinator::emitOptionsUsageString(out); 1334 out << " FQNAME...\n\n"; 1335 1336 out << "Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, to create output.\n\n"; 1337 1338 out.indent(); 1339 out.indent(); 1340 1341 out << "-h: Prints this menu.\n"; 1342 out << "-L <language>: The following options are available:\n"; 1343 out.indent([&] { 1344 for (auto& e : kFormats) { 1345 std::stringstream sstream; 1346 sstream.fill(' '); 1347 sstream.width(16); 1348 sstream << std::left << e.name(); 1349 1350 out << sstream.str() << ": " << e.description() << "\n"; 1351 } 1352 }); 1353 out << "-O <owner>: The owner of the module for -Landroidbp(-impl)?.\n"; 1354 out << "-o <output path>: Location to output files.\n"; 1355 Coordinator::emitOptionsDetailString(out); 1356 1357 out.unindent(); 1358 out.unindent(); 1359 } 1360 1361 // hidl is intentionally leaky. Turn off LeakSanitizer by default. 1362 extern "C" const char *__asan_default_options() { 1363 return "detect_leaks=0"; 1364 } 1365 1366 int main(int argc, char **argv) { 1367 const char *me = argv[0]; 1368 if (argc == 1) { 1369 usage(me); 1370 exit(1); 1371 } 1372 1373 const OutputHandler* outputFormat = nullptr; 1374 Coordinator coordinator; 1375 std::string outputPath; 1376 1377 coordinator.parseOptions(argc, argv, "ho:O:L:", [&](int res, char* arg) { 1378 switch (res) { 1379 case 'o': { 1380 if (!outputPath.empty()) { 1381 fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n"); 1382 exit(1); 1383 } 1384 outputPath = arg; 1385 break; 1386 } 1387 1388 case 'O': { 1389 if (!coordinator.getOwner().empty()) { 1390 fprintf(stderr, "ERROR: -O <owner> can only be specified once.\n"); 1391 exit(1); 1392 } 1393 coordinator.setOwner(arg); 1394 break; 1395 } 1396 1397 case 'L': { 1398 if (outputFormat != nullptr) { 1399 fprintf(stderr, 1400 "ERROR: only one -L option allowed. \"%s\" already specified.\n", 1401 outputFormat->name().c_str()); 1402 exit(1); 1403 } 1404 for (auto& e : kFormats) { 1405 if (e.name() == arg) { 1406 outputFormat = &e; 1407 break; 1408 } 1409 } 1410 if (outputFormat == nullptr) { 1411 fprintf(stderr, "ERROR: unrecognized -L option: \"%s\".\n", arg); 1412 exit(1); 1413 } 1414 break; 1415 } 1416 1417 case '?': 1418 case 'h': 1419 default: { 1420 usage(me); 1421 exit(1); 1422 break; 1423 } 1424 } 1425 }); 1426 1427 if (outputFormat == nullptr) { 1428 fprintf(stderr, 1429 "ERROR: no -L option provided.\n"); 1430 exit(1); 1431 } 1432 1433 argc -= optind; 1434 argv += optind; 1435 1436 if (argc == 0) { 1437 fprintf(stderr, "ERROR: no fqname specified.\n"); 1438 usage(me); 1439 exit(1); 1440 } 1441 1442 // Valid options are now in argv[0] .. argv[argc - 1]. 1443 1444 switch (outputFormat->mOutputMode) { 1445 case OutputMode::NEEDS_DIR: 1446 case OutputMode::NEEDS_FILE: { 1447 if (outputPath.empty()) { 1448 usage(me); 1449 exit(1); 1450 } 1451 1452 if (outputFormat->mOutputMode == OutputMode::NEEDS_DIR) { 1453 if (outputPath.back() != '/') { 1454 outputPath += "/"; 1455 } 1456 } 1457 break; 1458 } 1459 case OutputMode::NEEDS_SRC: { 1460 if (outputPath.empty()) { 1461 outputPath = coordinator.getRootPath(); 1462 } 1463 if (outputPath.back() != '/') { 1464 outputPath += "/"; 1465 } 1466 1467 break; 1468 } 1469 1470 default: 1471 outputPath.clear(); // Unused. 1472 break; 1473 } 1474 1475 coordinator.setOutputPath(outputPath); 1476 1477 for (int i = 0; i < argc; ++i) { 1478 const char* arg = argv[i]; 1479 1480 FQName fqName; 1481 if (!FQName::parse(arg, &fqName)) { 1482 fprintf(stderr, "ERROR: Invalid fully-qualified name as argument: %s.\n", arg); 1483 exit(1); 1484 } 1485 1486 if (coordinator.getPackageInterfaceFiles(fqName, nullptr /*fileNames*/) != OK) { 1487 fprintf(stderr, "ERROR: Could not get sources for %s.\n", arg); 1488 exit(1); 1489 } 1490 1491 // Dump extra verbose output 1492 if (coordinator.isVerbose()) { 1493 status_t err = 1494 dumpDefinedButUnreferencedTypeNames(fqName.getPackageAndVersion(), &coordinator); 1495 if (err != OK) return err; 1496 } 1497 1498 if (!outputFormat->validate(fqName, &coordinator, outputFormat->name())) { 1499 fprintf(stderr, 1500 "ERROR: output handler failed.\n"); 1501 exit(1); 1502 } 1503 1504 status_t err = outputFormat->generate(fqName, &coordinator); 1505 if (err != OK) exit(1); 1506 1507 err = outputFormat->writeDepFile(fqName, &coordinator); 1508 if (err != OK) exit(1); 1509 } 1510 1511 return 0; 1512 } 1513