| 1 | //===-- Options.cpp -------------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "Options.h" |
| 10 | #include "clang/Basic/DiagnosticIDs.h" |
| 11 | #include "clang/Driver/Driver.h" |
| 12 | #include "clang/InstallAPI/DirectoryScanner.h" |
| 13 | #include "clang/InstallAPI/FileList.h" |
| 14 | #include "clang/InstallAPI/HeaderFile.h" |
| 15 | #include "clang/InstallAPI/InstallAPIDiagnostic.h" |
| 16 | #include "llvm/BinaryFormat/Magic.h" |
| 17 | #include "llvm/Support/JSON.h" |
| 18 | #include "llvm/Support/Program.h" |
| 19 | #include "llvm/TargetParser/Host.h" |
| 20 | #include "llvm/TextAPI/DylibReader.h" |
| 21 | #include "llvm/TextAPI/TextAPIError.h" |
| 22 | #include "llvm/TextAPI/TextAPIReader.h" |
| 23 | #include "llvm/TextAPI/TextAPIWriter.h" |
| 24 | |
| 25 | using namespace llvm; |
| 26 | using namespace llvm::opt; |
| 27 | using namespace llvm::MachO; |
| 28 | |
| 29 | namespace clang { |
| 30 | namespace installapi { |
| 31 | |
| 32 | #define OPTTABLE_STR_TABLE_CODE |
| 33 | #include "InstallAPIOpts.inc" |
| 34 | #undef OPTTABLE_STR_TABLE_CODE |
| 35 | |
| 36 | #define OPTTABLE_PREFIXES_TABLE_CODE |
| 37 | #include "InstallAPIOpts.inc" |
| 38 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
| 39 | |
| 40 | #define OPTTABLE_PREFIXES_UNION_CODE |
| 41 | #include "InstallAPIOpts.inc" |
| 42 | #undef OPTTABLE_PREFIXES_UNION_CODE |
| 43 | |
| 44 | /// Create table mapping all options defined in InstallAPIOpts.td. |
| 45 | static constexpr OptTable::Info InfoTable[] = { |
| 46 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
| 47 | #include "InstallAPIOpts.inc" |
| 48 | #undef OPTION |
| 49 | }; |
| 50 | |
| 51 | namespace { |
| 52 | |
| 53 | /// \brief Create OptTable class for parsing actual command line arguments. |
| 54 | class DriverOptTable : public opt::PrecomputedOptTable { |
| 55 | public: |
| 56 | DriverOptTable() |
| 57 | : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, |
| 58 | OptionPrefixesUnion) {} |
| 59 | }; |
| 60 | |
| 61 | } // end anonymous namespace. |
| 62 | |
| 63 | static llvm::opt::OptTable *createDriverOptTable() { |
| 64 | return new DriverOptTable(); |
| 65 | } |
| 66 | |
| 67 | /// Parse JSON input into argument list. |
| 68 | /// |
| 69 | /* Expected input format. |
| 70 | * { "label" : ["-ClangArg1", "-ClangArg2"] } |
| 71 | */ |
| 72 | /// |
| 73 | /// Input is interpreted as "-Xlabel ClangArg1 -XLabel ClangArg2". |
| 74 | static Expected<llvm::opt::InputArgList> |
| 75 | getArgListFromJSON(const StringRef Input, llvm::opt::OptTable *Table, |
| 76 | std::vector<std::string> &Storage) { |
| 77 | using namespace json; |
| 78 | Expected<Value> ValOrErr = json::parse(JSON: Input); |
| 79 | if (!ValOrErr) |
| 80 | return ValOrErr.takeError(); |
| 81 | |
| 82 | const Object *Root = ValOrErr->getAsObject(); |
| 83 | if (!Root) |
| 84 | return llvm::opt::InputArgList(); |
| 85 | |
| 86 | for (const auto &KV : *Root) { |
| 87 | const Array *ArgList = KV.getSecond().getAsArray(); |
| 88 | std::string Label = "-X" + KV.getFirst().str(); |
| 89 | if (!ArgList) |
| 90 | return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat); |
| 91 | for (auto Arg : *ArgList) { |
| 92 | std::optional<StringRef> ArgStr = Arg.getAsString(); |
| 93 | if (!ArgStr) |
| 94 | return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat); |
| 95 | Storage.emplace_back(args&: Label); |
| 96 | Storage.emplace_back(args&: *ArgStr); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | std::vector<const char *> CArgs(Storage.size()); |
| 101 | for (StringRef Str : Storage) |
| 102 | CArgs.emplace_back(args: Str.data()); |
| 103 | |
| 104 | unsigned MissingArgIndex, MissingArgCount; |
| 105 | return Table->ParseArgs(Args: CArgs, MissingArgIndex, MissingArgCount); |
| 106 | } |
| 107 | |
| 108 | bool Options::processDriverOptions(InputArgList &Args) { |
| 109 | // Handle inputs. |
| 110 | for (const StringRef Path : Args.getAllArgValues(Id: options::OPT_INPUT)) { |
| 111 | // Assume any input that is not a directory is a filelist. |
| 112 | // InstallAPI does not accept multiple directories, so retain the last one. |
| 113 | if (FM->getOptionalDirectoryRef(DirName: Path)) |
| 114 | DriverOpts.InputDirectory = Path.str(); |
| 115 | else |
| 116 | DriverOpts.FileLists.emplace_back(args: Path.str()); |
| 117 | } |
| 118 | |
| 119 | // Handle output. |
| 120 | SmallString<PATH_MAX> OutputPath; |
| 121 | if (auto *Arg = Args.getLastArg(Ids: options::OPT_o)) { |
| 122 | OutputPath = Arg->getValue(); |
| 123 | if (OutputPath != "-" ) |
| 124 | FM->makeAbsolutePath(Path&: OutputPath); |
| 125 | DriverOpts.OutputPath = std::string(OutputPath); |
| 126 | } |
| 127 | if (DriverOpts.OutputPath.empty()) { |
| 128 | Diags->Report(DiagID: diag::err_no_output_file); |
| 129 | return false; |
| 130 | } |
| 131 | |
| 132 | // Do basic error checking first for mixing -target and -arch options. |
| 133 | auto *ArgArch = Args.getLastArgNoClaim(Ids: options::OPT_arch); |
| 134 | auto *ArgTarget = Args.getLastArgNoClaim(Ids: options::OPT_target); |
| 135 | auto *ArgTargetVariant = |
| 136 | Args.getLastArgNoClaim(Ids: options::OPT_darwin_target_variant); |
| 137 | if (ArgArch && (ArgTarget || ArgTargetVariant)) { |
| 138 | Diags->Report(DiagID: clang::diag::err_drv_argument_not_allowed_with) |
| 139 | << ArgArch->getAsString(Args) |
| 140 | << (ArgTarget ? ArgTarget : ArgTargetVariant)->getAsString(Args); |
| 141 | return false; |
| 142 | } |
| 143 | |
| 144 | auto *ArgMinTargetOS = Args.getLastArgNoClaim(Ids: options::OPT_mtargetos_EQ); |
| 145 | if ((ArgTarget || ArgTargetVariant) && ArgMinTargetOS) { |
| 146 | Diags->Report(DiagID: clang::diag::err_drv_cannot_mix_options) |
| 147 | << ArgTarget->getAsString(Args) << ArgMinTargetOS->getAsString(Args); |
| 148 | return false; |
| 149 | } |
| 150 | |
| 151 | // Capture target triples first. |
| 152 | if (ArgTarget) { |
| 153 | for (const Arg *A : Args.filtered(Ids: options::OPT_target)) { |
| 154 | A->claim(); |
| 155 | llvm::Triple TargetTriple(A->getValue()); |
| 156 | Target TAPITarget = Target(TargetTriple); |
| 157 | if ((TAPITarget.Arch == AK_unknown) || |
| 158 | (TAPITarget.Platform == PLATFORM_UNKNOWN)) { |
| 159 | Diags->Report(DiagID: clang::diag::err_drv_unsupported_opt_for_target) |
| 160 | << "installapi" << TargetTriple.str(); |
| 161 | return false; |
| 162 | } |
| 163 | DriverOpts.Targets[TAPITarget] = TargetTriple; |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | // Capture target variants. |
| 168 | DriverOpts.Zippered = ArgTargetVariant != nullptr; |
| 169 | for (Arg *A : Args.filtered(Ids: options::OPT_darwin_target_variant)) { |
| 170 | A->claim(); |
| 171 | Triple Variant(A->getValue()); |
| 172 | if (Variant.getVendor() != Triple::Apple) { |
| 173 | Diags->Report(DiagID: diag::err_unsupported_vendor) |
| 174 | << Variant.getVendorName() << A->getAsString(Args); |
| 175 | return false; |
| 176 | } |
| 177 | |
| 178 | switch (Variant.getOS()) { |
| 179 | default: |
| 180 | Diags->Report(DiagID: diag::err_unsupported_os) |
| 181 | << Variant.getOSName() << A->getAsString(Args); |
| 182 | return false; |
| 183 | case Triple::MacOSX: |
| 184 | case Triple::IOS: |
| 185 | break; |
| 186 | } |
| 187 | |
| 188 | switch (Variant.getEnvironment()) { |
| 189 | default: |
| 190 | Diags->Report(DiagID: diag::err_unsupported_environment) |
| 191 | << Variant.getEnvironmentName() << A->getAsString(Args); |
| 192 | return false; |
| 193 | case Triple::UnknownEnvironment: |
| 194 | case Triple::MacABI: |
| 195 | break; |
| 196 | } |
| 197 | |
| 198 | Target TAPIVariant(Variant); |
| 199 | // See if there is a matching --target option for this --target-variant |
| 200 | // option. |
| 201 | auto It = find_if(Range&: DriverOpts.Targets, P: [&](const auto &T) { |
| 202 | return (T.first.Arch == TAPIVariant.Arch) && |
| 203 | (T.first.Platform != PlatformType::PLATFORM_UNKNOWN); |
| 204 | }); |
| 205 | |
| 206 | if (It == DriverOpts.Targets.end()) { |
| 207 | Diags->Report(DiagID: diag::err_no_matching_target) << Variant.str(); |
| 208 | return false; |
| 209 | } |
| 210 | |
| 211 | DriverOpts.Targets[TAPIVariant] = Variant; |
| 212 | } |
| 213 | |
| 214 | DriverOpts.Verbose = Args.hasArgNoClaim(Ids: options::OPT_v); |
| 215 | |
| 216 | return true; |
| 217 | } |
| 218 | |
| 219 | bool Options::processInstallAPIXOptions(InputArgList &Args) { |
| 220 | for (arg_iterator It = Args.begin(), End = Args.end(); It != End; ++It) { |
| 221 | Arg *A = *It; |
| 222 | if (A->getOption().matches(ID: OPT_Xarch__)) { |
| 223 | if (!processXarchOption(Args, Curr: It)) |
| 224 | return false; |
| 225 | continue; |
| 226 | } else if (A->getOption().matches(ID: OPT_Xplatform__)) { |
| 227 | if (!processXplatformOption(Args, Curr: It)) |
| 228 | return false; |
| 229 | continue; |
| 230 | } else if (A->getOption().matches(ID: OPT_Xproject)) { |
| 231 | if (!processXprojectOption(Args, Curr: It)) |
| 232 | return false; |
| 233 | continue; |
| 234 | } else if (!A->getOption().matches(ID: OPT_X__)) |
| 235 | continue; |
| 236 | |
| 237 | // Handle any user defined labels. |
| 238 | const StringRef Label = A->getValue(N: 0); |
| 239 | |
| 240 | // Ban "public" and "private" labels. |
| 241 | if ((Label.lower() == "public" ) || (Label.lower() == "private" )) { |
| 242 | Diags->Report(DiagID: diag::err_invalid_label) << Label; |
| 243 | return false; |
| 244 | } |
| 245 | |
| 246 | auto NextIt = std::next(x: It); |
| 247 | if (NextIt == End) { |
| 248 | Diags->Report(DiagID: clang::diag::err_drv_missing_argument) |
| 249 | << A->getAsString(Args) << 1; |
| 250 | return false; |
| 251 | } |
| 252 | Arg *NextA = *NextIt; |
| 253 | switch ((ID)NextA->getOption().getID()) { |
| 254 | case OPT_D: |
| 255 | case OPT_U: |
| 256 | break; |
| 257 | default: |
| 258 | Diags->Report(DiagID: clang::diag::err_drv_argument_not_allowed_with) |
| 259 | << A->getAsString(Args) << NextA->getAsString(Args); |
| 260 | return false; |
| 261 | } |
| 262 | const StringRef ASpelling = NextA->getSpelling(); |
| 263 | const auto &AValues = NextA->getValues(); |
| 264 | auto &UniqueArgs = FEOpts.UniqueArgs[Label]; |
| 265 | if (AValues.empty()) |
| 266 | UniqueArgs.emplace_back(args: ASpelling.str()); |
| 267 | else |
| 268 | for (const StringRef Val : AValues) |
| 269 | UniqueArgs.emplace_back(args: (ASpelling + Val).str()); |
| 270 | |
| 271 | A->claim(); |
| 272 | NextA->claim(); |
| 273 | } |
| 274 | |
| 275 | return true; |
| 276 | } |
| 277 | |
| 278 | bool Options::processXplatformOption(InputArgList &Args, arg_iterator Curr) { |
| 279 | Arg *A = *Curr; |
| 280 | |
| 281 | PlatformType Platform = getPlatformFromName(Name: A->getValue(N: 0)); |
| 282 | if (Platform == PLATFORM_UNKNOWN) { |
| 283 | Diags->Report(DiagID: diag::err_unsupported_os) |
| 284 | << getPlatformName(Platform) << A->getAsString(Args); |
| 285 | return false; |
| 286 | } |
| 287 | auto NextIt = std::next(x: Curr); |
| 288 | if (NextIt == Args.end()) { |
| 289 | Diags->Report(DiagID: diag::err_drv_missing_argument) << A->getAsString(Args) << 1; |
| 290 | return false; |
| 291 | } |
| 292 | |
| 293 | Arg *NextA = *NextIt; |
| 294 | switch ((ID)NextA->getOption().getID()) { |
| 295 | case OPT_iframework: |
| 296 | FEOpts.SystemFwkPaths.emplace_back(args: NextA->getValue(), args&: Platform); |
| 297 | break; |
| 298 | default: |
| 299 | Diags->Report(DiagID: diag::err_drv_invalid_argument_to_option) |
| 300 | << A->getAsString(Args) << NextA->getAsString(Args); |
| 301 | return false; |
| 302 | } |
| 303 | |
| 304 | A->claim(); |
| 305 | NextA->claim(); |
| 306 | |
| 307 | return true; |
| 308 | } |
| 309 | |
| 310 | bool Options::processXprojectOption(InputArgList &Args, arg_iterator Curr) { |
| 311 | Arg *A = *Curr; |
| 312 | auto NextIt = std::next(x: Curr); |
| 313 | if (NextIt == Args.end()) { |
| 314 | Diags->Report(DiagID: diag::err_drv_missing_argument) << A->getAsString(Args) << 1; |
| 315 | return false; |
| 316 | } |
| 317 | |
| 318 | Arg *NextA = *NextIt; |
| 319 | switch ((ID)NextA->getOption().getID()) { |
| 320 | case OPT_fobjc_arc: |
| 321 | case OPT_fmodules: |
| 322 | case OPT_fmodules_cache_path: |
| 323 | case OPT_include_: |
| 324 | case OPT_fvisibility_EQ: |
| 325 | break; |
| 326 | default: |
| 327 | Diags->Report(DiagID: diag::err_drv_argument_not_allowed_with) |
| 328 | << A->getAsString(Args) << NextA->getAsString(Args); |
| 329 | return false; |
| 330 | } |
| 331 | |
| 332 | std::string ArgString = NextA->getSpelling().str(); |
| 333 | for (const StringRef Val : NextA->getValues()) |
| 334 | ArgString += Val.str(); |
| 335 | |
| 336 | ProjectLevelArgs.push_back(x: ArgString); |
| 337 | A->claim(); |
| 338 | NextA->claim(); |
| 339 | |
| 340 | return true; |
| 341 | } |
| 342 | |
| 343 | bool Options::processXarchOption(InputArgList &Args, arg_iterator Curr) { |
| 344 | Arg *CurrArg = *Curr; |
| 345 | Architecture Arch = getArchitectureFromName(Name: CurrArg->getValue(N: 0)); |
| 346 | if (Arch == AK_unknown) { |
| 347 | Diags->Report(DiagID: diag::err_drv_invalid_arch_name) |
| 348 | << CurrArg->getAsString(Args); |
| 349 | return false; |
| 350 | } |
| 351 | |
| 352 | auto NextIt = std::next(x: Curr); |
| 353 | if (NextIt == Args.end()) { |
| 354 | Diags->Report(DiagID: diag::err_drv_missing_argument) |
| 355 | << CurrArg->getAsString(Args) << 1; |
| 356 | return false; |
| 357 | } |
| 358 | |
| 359 | // InstallAPI has a limited understanding of supported Xarch options. |
| 360 | // Currently this is restricted to linker inputs. |
| 361 | const Arg *NextArg = *NextIt; |
| 362 | switch (NextArg->getOption().getID()) { |
| 363 | case OPT_allowable_client: |
| 364 | case OPT_reexport_l: |
| 365 | case OPT_reexport_framework: |
| 366 | case OPT_reexport_library: |
| 367 | case OPT_rpath: |
| 368 | break; |
| 369 | default: |
| 370 | Diags->Report(DiagID: diag::err_drv_invalid_argument_to_option) |
| 371 | << NextArg->getAsString(Args) << CurrArg->getAsString(Args); |
| 372 | return false; |
| 373 | } |
| 374 | |
| 375 | ArgToArchMap[NextArg] = Arch; |
| 376 | CurrArg->claim(); |
| 377 | |
| 378 | return true; |
| 379 | } |
| 380 | |
| 381 | bool Options::processOptionList(InputArgList &Args, |
| 382 | llvm::opt::OptTable *Table) { |
| 383 | Arg *A = Args.getLastArg(Ids: OPT_option_list); |
| 384 | if (!A) |
| 385 | return true; |
| 386 | |
| 387 | const StringRef Path = A->getValue(N: 0); |
| 388 | auto InputOrErr = FM->getBufferForFile(Filename: Path); |
| 389 | if (auto Err = InputOrErr.getError()) { |
| 390 | Diags->Report(DiagID: diag::err_cannot_open_file) << Path << Err.message(); |
| 391 | return false; |
| 392 | } |
| 393 | // Backing storage referenced for argument processing. |
| 394 | std::vector<std::string> Storage; |
| 395 | auto ArgsOrErr = |
| 396 | getArgListFromJSON(Input: (*InputOrErr)->getBuffer(), Table, Storage); |
| 397 | |
| 398 | if (auto Err = ArgsOrErr.takeError()) { |
| 399 | Diags->Report(DiagID: diag::err_cannot_read_input_list) |
| 400 | << "option" << Path << toString(E: std::move(Err)); |
| 401 | return false; |
| 402 | } |
| 403 | return processInstallAPIXOptions(Args&: *ArgsOrErr); |
| 404 | } |
| 405 | |
| 406 | bool Options::processLinkerOptions(InputArgList &Args) { |
| 407 | // Handle required arguments. |
| 408 | if (const Arg *A = Args.getLastArg(Ids: options::OPT_install__name)) |
| 409 | LinkerOpts.InstallName = A->getValue(); |
| 410 | if (LinkerOpts.InstallName.empty()) { |
| 411 | Diags->Report(DiagID: diag::err_no_install_name); |
| 412 | return false; |
| 413 | } |
| 414 | |
| 415 | // Defaulted or optional arguments. |
| 416 | if (auto *Arg = Args.getLastArg(Ids: options::OPT_current__version)) |
| 417 | LinkerOpts.CurrentVersion.parse64(Str: Arg->getValue()); |
| 418 | |
| 419 | if (auto *Arg = Args.getLastArg(Ids: options::OPT_compatibility__version)) |
| 420 | LinkerOpts.CompatVersion.parse64(Str: Arg->getValue()); |
| 421 | |
| 422 | if (auto *Arg = Args.getLastArg(Ids: options::OPT_compatibility__version)) |
| 423 | LinkerOpts.CompatVersion.parse64(Str: Arg->getValue()); |
| 424 | |
| 425 | if (auto *Arg = Args.getLastArg(Ids: options::OPT_umbrella)) |
| 426 | LinkerOpts.ParentUmbrella = Arg->getValue(); |
| 427 | |
| 428 | LinkerOpts.IsDylib = Args.hasArg(Ids: options::OPT_dynamiclib); |
| 429 | |
| 430 | for (auto *Arg : Args.filtered(Ids: options::OPT_alias_list)) { |
| 431 | LinkerOpts.AliasLists.emplace_back(args: Arg->getValue()); |
| 432 | Arg->claim(); |
| 433 | } |
| 434 | |
| 435 | LinkerOpts.AppExtensionSafe = |
| 436 | Args.hasFlag(Pos: options::OPT_fapplication_extension, |
| 437 | Neg: options::OPT_fno_application_extension, |
| 438 | /*Default=*/LinkerOpts.AppExtensionSafe); |
| 439 | |
| 440 | if (::getenv(name: "LD_NO_ENCRYPT" ) != nullptr) |
| 441 | LinkerOpts.AppExtensionSafe = true; |
| 442 | |
| 443 | if (::getenv(name: "LD_APPLICATION_EXTENSION_SAFE" ) != nullptr) |
| 444 | LinkerOpts.AppExtensionSafe = true; |
| 445 | |
| 446 | // Capture library paths. |
| 447 | PathSeq LibraryPaths; |
| 448 | for (const Arg *A : Args.filtered(Ids: options::OPT_L)) { |
| 449 | LibraryPaths.emplace_back(args: A->getValue()); |
| 450 | A->claim(); |
| 451 | } |
| 452 | |
| 453 | if (!LibraryPaths.empty()) |
| 454 | LinkerOpts.LibPaths = std::move(LibraryPaths); |
| 455 | |
| 456 | return true; |
| 457 | } |
| 458 | |
| 459 | // NOTE: Do not claim any arguments, as they will be passed along for CC1 |
| 460 | // invocations. |
| 461 | bool Options::processFrontendOptions(InputArgList &Args) { |
| 462 | // Capture language mode. |
| 463 | if (auto *A = Args.getLastArgNoClaim(Ids: options::OPT_x)) { |
| 464 | FEOpts.LangMode = llvm::StringSwitch<clang::Language>(A->getValue()) |
| 465 | .Case(S: "c" , Value: clang::Language::C) |
| 466 | .Case(S: "c++" , Value: clang::Language::CXX) |
| 467 | .Case(S: "objective-c" , Value: clang::Language::ObjC) |
| 468 | .Case(S: "objective-c++" , Value: clang::Language::ObjCXX) |
| 469 | .Default(Value: clang::Language::Unknown); |
| 470 | |
| 471 | if (FEOpts.LangMode == clang::Language::Unknown) { |
| 472 | Diags->Report(DiagID: clang::diag::err_drv_invalid_value) |
| 473 | << A->getAsString(Args) << A->getValue(); |
| 474 | return false; |
| 475 | } |
| 476 | } |
| 477 | for (auto *A : Args.filtered(Ids: options::OPT_ObjC, Ids: options::OPT_ObjCXX)) { |
| 478 | if (A->getOption().matches(ID: options::OPT_ObjC)) |
| 479 | FEOpts.LangMode = clang::Language::ObjC; |
| 480 | else |
| 481 | FEOpts.LangMode = clang::Language::ObjCXX; |
| 482 | } |
| 483 | |
| 484 | // Capture Sysroot. |
| 485 | if (const Arg *A = Args.getLastArgNoClaim(Ids: options::OPT_isysroot)) { |
| 486 | SmallString<PATH_MAX> Path(A->getValue()); |
| 487 | FM->makeAbsolutePath(Path); |
| 488 | if (!FM->getOptionalDirectoryRef(DirName: Path)) { |
| 489 | Diags->Report(DiagID: diag::err_missing_sysroot) << Path; |
| 490 | return false; |
| 491 | } |
| 492 | FEOpts.ISysroot = std::string(Path); |
| 493 | } else if (FEOpts.ISysroot.empty()) { |
| 494 | // Mirror CLANG and obtain the isysroot from the SDKROOT environment |
| 495 | // variable, if it wasn't defined by the command line. |
| 496 | if (auto *Env = ::getenv(name: "SDKROOT" )) { |
| 497 | if (StringRef(Env) != "/" && llvm::sys::path::is_absolute(path: Env) && |
| 498 | FM->getOptionalFileRef(Filename: Env)) |
| 499 | FEOpts.ISysroot = Env; |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | // Capture system frameworks for all platforms. |
| 504 | for (const Arg *A : Args.filtered(Ids: options::OPT_iframework)) |
| 505 | FEOpts.SystemFwkPaths.emplace_back(args: A->getValue(), |
| 506 | args: std::optional<PlatformType>{}); |
| 507 | |
| 508 | // Capture framework paths. |
| 509 | PathSeq FrameworkPaths; |
| 510 | for (const Arg *A : Args.filtered(Ids: options::OPT_F)) |
| 511 | FrameworkPaths.emplace_back(args: A->getValue()); |
| 512 | |
| 513 | if (!FrameworkPaths.empty()) |
| 514 | FEOpts.FwkPaths = std::move(FrameworkPaths); |
| 515 | |
| 516 | // Add default framework/library paths. |
| 517 | PathSeq DefaultLibraryPaths = {"/usr/lib" , "/usr/local/lib" }; |
| 518 | PathSeq DefaultFrameworkPaths = {"/Library/Frameworks" , |
| 519 | "/System/Library/Frameworks" }; |
| 520 | |
| 521 | for (const StringRef LibPath : DefaultLibraryPaths) { |
| 522 | SmallString<PATH_MAX> Path(FEOpts.ISysroot); |
| 523 | sys::path::append(path&: Path, a: LibPath); |
| 524 | LinkerOpts.LibPaths.emplace_back(args: Path.str()); |
| 525 | } |
| 526 | for (const StringRef FwkPath : DefaultFrameworkPaths) { |
| 527 | SmallString<PATH_MAX> Path(FEOpts.ISysroot); |
| 528 | sys::path::append(path&: Path, a: FwkPath); |
| 529 | FEOpts.SystemFwkPaths.emplace_back(args: Path.str(), |
| 530 | args: std::optional<PlatformType>{}); |
| 531 | } |
| 532 | |
| 533 | return true; |
| 534 | } |
| 535 | |
| 536 | bool Options::addFilePaths(InputArgList &Args, PathSeq &, |
| 537 | OptSpecifier ID) { |
| 538 | for (const StringRef Path : Args.getAllArgValues(Id: ID)) { |
| 539 | if ((bool)FM->getOptionalDirectoryRef(DirName: Path, /*CacheFailure=*/false)) { |
| 540 | auto = enumerateFiles(FM&: *FM, Directory: Path); |
| 541 | if (!InputHeadersOrErr) { |
| 542 | Diags->Report(DiagID: diag::err_cannot_open_file) |
| 543 | << Path << toString(E: InputHeadersOrErr.takeError()); |
| 544 | return false; |
| 545 | } |
| 546 | // Sort headers to ensure deterministic behavior. |
| 547 | sort(C&: *InputHeadersOrErr); |
| 548 | for (StringRef H : *InputHeadersOrErr) |
| 549 | Headers.emplace_back(args: std::move(H)); |
| 550 | } else |
| 551 | Headers.emplace_back(args: Path); |
| 552 | } |
| 553 | return true; |
| 554 | } |
| 555 | |
| 556 | std::vector<const char *> |
| 557 | Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) { |
| 558 | std::unique_ptr<llvm::opt::OptTable> Table; |
| 559 | Table.reset(p: createDriverOptTable()); |
| 560 | |
| 561 | unsigned MissingArgIndex, MissingArgCount; |
| 562 | auto ParsedArgs = Table->ParseArgs(Args: Args.slice(N: 1), MissingArgIndex, |
| 563 | MissingArgCount, FlagsToInclude: Visibility()); |
| 564 | |
| 565 | // Capture InstallAPI only driver options. |
| 566 | if (!processInstallAPIXOptions(Args&: ParsedArgs)) |
| 567 | return {}; |
| 568 | |
| 569 | if (!processOptionList(Args&: ParsedArgs, Table: Table.get())) |
| 570 | return {}; |
| 571 | |
| 572 | DriverOpts.Demangle = ParsedArgs.hasArg(Ids: OPT_demangle); |
| 573 | |
| 574 | if (auto *A = ParsedArgs.getLastArg(Ids: OPT_filetype)) { |
| 575 | DriverOpts.OutFT = TextAPIWriter::parseFileType(FT: A->getValue()); |
| 576 | if (DriverOpts.OutFT == FileType::Invalid) { |
| 577 | Diags->Report(DiagID: clang::diag::err_drv_invalid_value) |
| 578 | << A->getAsString(Args: ParsedArgs) << A->getValue(); |
| 579 | return {}; |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_verify_mode_EQ)) { |
| 584 | DriverOpts.VerifyMode = |
| 585 | StringSwitch<VerificationMode>(A->getValue()) |
| 586 | .Case(S: "ErrorsOnly" , Value: VerificationMode::ErrorsOnly) |
| 587 | .Case(S: "ErrorsAndWarnings" , Value: VerificationMode::ErrorsAndWarnings) |
| 588 | .Case(S: "Pedantic" , Value: VerificationMode::Pedantic) |
| 589 | .Default(Value: VerificationMode::Invalid); |
| 590 | |
| 591 | if (DriverOpts.VerifyMode == VerificationMode::Invalid) { |
| 592 | Diags->Report(DiagID: clang::diag::err_drv_invalid_value) |
| 593 | << A->getAsString(Args: ParsedArgs) << A->getValue(); |
| 594 | return {}; |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_verify_against)) |
| 599 | DriverOpts.DylibToVerify = A->getValue(); |
| 600 | |
| 601 | if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_dsym)) |
| 602 | DriverOpts.DSYMPath = A->getValue(); |
| 603 | |
| 604 | DriverOpts.TraceLibraryLocation = ParsedArgs.hasArg(Ids: OPT_t); |
| 605 | |
| 606 | // Linker options not handled by clang driver. |
| 607 | LinkerOpts.OSLibNotForSharedCache = |
| 608 | ParsedArgs.hasArg(Ids: OPT_not_for_dyld_shared_cache); |
| 609 | |
| 610 | for (const Arg *A : ParsedArgs.filtered(Ids: OPT_allowable_client)) { |
| 611 | auto It = ArgToArchMap.find(Val: A); |
| 612 | LinkerOpts.AllowableClients.getArchSet(Attr: A->getValue()) = |
| 613 | It != ArgToArchMap.end() ? It->second : ArchitectureSet(); |
| 614 | A->claim(); |
| 615 | } |
| 616 | |
| 617 | for (const Arg *A : ParsedArgs.filtered(Ids: OPT_reexport_l)) { |
| 618 | auto It = ArgToArchMap.find(Val: A); |
| 619 | LinkerOpts.ReexportedLibraries.getArchSet(Attr: A->getValue()) = |
| 620 | It != ArgToArchMap.end() ? It->second : ArchitectureSet(); |
| 621 | A->claim(); |
| 622 | } |
| 623 | |
| 624 | for (const Arg *A : ParsedArgs.filtered(Ids: OPT_reexport_library)) { |
| 625 | auto It = ArgToArchMap.find(Val: A); |
| 626 | LinkerOpts.ReexportedLibraryPaths.getArchSet(Attr: A->getValue()) = |
| 627 | It != ArgToArchMap.end() ? It->second : ArchitectureSet(); |
| 628 | A->claim(); |
| 629 | } |
| 630 | |
| 631 | for (const Arg *A : ParsedArgs.filtered(Ids: OPT_reexport_framework)) { |
| 632 | auto It = ArgToArchMap.find(Val: A); |
| 633 | LinkerOpts.ReexportedFrameworks.getArchSet(Attr: A->getValue()) = |
| 634 | It != ArgToArchMap.end() ? It->second : ArchitectureSet(); |
| 635 | A->claim(); |
| 636 | } |
| 637 | |
| 638 | for (const Arg *A : ParsedArgs.filtered(Ids: OPT_rpath)) { |
| 639 | auto It = ArgToArchMap.find(Val: A); |
| 640 | LinkerOpts.RPaths.getArchSet(Attr: A->getValue()) = |
| 641 | It != ArgToArchMap.end() ? It->second : ArchitectureSet(); |
| 642 | A->claim(); |
| 643 | } |
| 644 | |
| 645 | // Handle exclude & extra header directories or files. |
| 646 | auto handleAdditionalInputArgs = [&](PathSeq &, |
| 647 | clang::installapi::ID OptID) { |
| 648 | if (ParsedArgs.hasArgNoClaim(Ids: OptID)) |
| 649 | Headers.clear(); |
| 650 | return addFilePaths(Args&: ParsedArgs, Headers, ID: OptID); |
| 651 | }; |
| 652 | |
| 653 | if (!handleAdditionalInputArgs(DriverOpts.ExtraPublicHeaders, |
| 654 | OPT_extra_public_header)) |
| 655 | return {}; |
| 656 | |
| 657 | if (!handleAdditionalInputArgs(DriverOpts.ExtraPrivateHeaders, |
| 658 | OPT_extra_private_header)) |
| 659 | return {}; |
| 660 | if (!handleAdditionalInputArgs(DriverOpts.ExtraProjectHeaders, |
| 661 | OPT_extra_project_header)) |
| 662 | return {}; |
| 663 | |
| 664 | if (!handleAdditionalInputArgs(DriverOpts.ExcludePublicHeaders, |
| 665 | OPT_exclude_public_header)) |
| 666 | return {}; |
| 667 | if (!handleAdditionalInputArgs(DriverOpts.ExcludePrivateHeaders, |
| 668 | OPT_exclude_private_header)) |
| 669 | return {}; |
| 670 | if (!handleAdditionalInputArgs(DriverOpts.ExcludeProjectHeaders, |
| 671 | OPT_exclude_project_header)) |
| 672 | return {}; |
| 673 | |
| 674 | // Handle umbrella headers. |
| 675 | if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_public_umbrella_header)) |
| 676 | DriverOpts.PublicUmbrellaHeader = A->getValue(); |
| 677 | |
| 678 | if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_private_umbrella_header)) |
| 679 | DriverOpts.PrivateUmbrellaHeader = A->getValue(); |
| 680 | |
| 681 | if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_project_umbrella_header)) |
| 682 | DriverOpts.ProjectUmbrellaHeader = A->getValue(); |
| 683 | |
| 684 | /// Any unclaimed arguments should be forwarded to the clang driver. |
| 685 | std::vector<const char *> ClangDriverArgs(ParsedArgs.size()); |
| 686 | for (const Arg *A : ParsedArgs) { |
| 687 | if (A->isClaimed()) |
| 688 | continue; |
| 689 | // Forward along unclaimed but overlapping arguments to the clang driver. |
| 690 | if (A->getOption().getID() > (unsigned)OPT_UNKNOWN) { |
| 691 | ClangDriverArgs.push_back(x: A->getSpelling().data()); |
| 692 | } else |
| 693 | llvm::append_range(C&: ClangDriverArgs, R: A->getValues()); |
| 694 | } |
| 695 | return ClangDriverArgs; |
| 696 | } |
| 697 | |
| 698 | Options::Options(DiagnosticsEngine &Diag, FileManager *FM, |
| 699 | ArrayRef<const char *> Args, const StringRef ProgName) |
| 700 | : Diags(&Diag), FM(FM) { |
| 701 | |
| 702 | // First process InstallAPI specific options. |
| 703 | auto DriverArgs = processAndFilterOutInstallAPIOptions(Args); |
| 704 | if (Diags->hasErrorOccurred()) |
| 705 | return; |
| 706 | |
| 707 | // Set up driver to parse remaining input arguments. |
| 708 | clang::driver::Driver Driver(ProgName, llvm::sys::getDefaultTargetTriple(), |
| 709 | *Diags, "clang installapi tool" ); |
| 710 | auto TargetAndMode = |
| 711 | clang::driver::ToolChain::getTargetAndModeFromProgramName(ProgName); |
| 712 | Driver.setTargetAndMode(TargetAndMode); |
| 713 | bool HasError = false; |
| 714 | llvm::opt::InputArgList ArgList = |
| 715 | Driver.ParseArgStrings(Args: DriverArgs, /*UseDriverMode=*/true, ContainsError&: HasError); |
| 716 | if (HasError) |
| 717 | return; |
| 718 | Driver.setCheckInputsExist(false); |
| 719 | |
| 720 | if (!processDriverOptions(Args&: ArgList)) |
| 721 | return; |
| 722 | |
| 723 | if (!processLinkerOptions(Args&: ArgList)) |
| 724 | return; |
| 725 | |
| 726 | if (!processFrontendOptions(Args&: ArgList)) |
| 727 | return; |
| 728 | |
| 729 | // After all InstallAPI necessary arguments have been collected. Go back and |
| 730 | // assign values that were unknown before the clang driver opt table was used. |
| 731 | ArchitectureSet AllArchs; |
| 732 | for (const auto &T : DriverOpts.Targets) |
| 733 | AllArchs.set(T.first.Arch); |
| 734 | auto assignDefaultLibAttrs = [&AllArchs](LibAttrs &Attrs) { |
| 735 | for (auto &[_, Archs] : Attrs.get()) |
| 736 | if (Archs.empty()) |
| 737 | Archs = AllArchs; |
| 738 | }; |
| 739 | assignDefaultLibAttrs(LinkerOpts.AllowableClients); |
| 740 | assignDefaultLibAttrs(LinkerOpts.ReexportedFrameworks); |
| 741 | assignDefaultLibAttrs(LinkerOpts.ReexportedLibraries); |
| 742 | assignDefaultLibAttrs(LinkerOpts.ReexportedLibraryPaths); |
| 743 | assignDefaultLibAttrs(LinkerOpts.RPaths); |
| 744 | |
| 745 | /// Force cc1 options that should always be on. |
| 746 | FrontendArgs = {"-fsyntax-only" , "-Wprivate-extern" }; |
| 747 | |
| 748 | /// Any unclaimed arguments should be handled by invoking the clang frontend. |
| 749 | for (const Arg *A : ArgList) { |
| 750 | if (A->isClaimed()) |
| 751 | continue; |
| 752 | FrontendArgs.emplace_back(args: A->getSpelling()); |
| 753 | llvm::append_range(C&: FrontendArgs, R: A->getValues()); |
| 754 | } |
| 755 | } |
| 756 | |
| 757 | static Expected<std::unique_ptr<InterfaceFile>> |
| 758 | getInterfaceFile(const StringRef Filename) { |
| 759 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = |
| 760 | MemoryBuffer::getFile(Filename); |
| 761 | if (auto Err = BufferOrErr.getError()) |
| 762 | return errorCodeToError(EC: std::move(Err)); |
| 763 | |
| 764 | auto Buffer = std::move(*BufferOrErr); |
| 765 | switch (identify_magic(magic: Buffer->getBuffer())) { |
| 766 | case file_magic::macho_dynamically_linked_shared_lib: |
| 767 | case file_magic::macho_dynamically_linked_shared_lib_stub: |
| 768 | case file_magic::macho_universal_binary: |
| 769 | return DylibReader::get(Buffer: Buffer->getMemBufferRef()); |
| 770 | break; |
| 771 | case file_magic::tapi_file: |
| 772 | return TextAPIReader::get(InputBuffer: Buffer->getMemBufferRef()); |
| 773 | default: |
| 774 | return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat, |
| 775 | Args: "unsupported library file format" ); |
| 776 | } |
| 777 | llvm_unreachable("unexpected failure in getInterface" ); |
| 778 | } |
| 779 | |
| 780 | std::pair<LibAttrs, ReexportedInterfaces> Options::getReexportedLibraries() { |
| 781 | LibAttrs Reexports; |
| 782 | ReexportedInterfaces ReexportIFs; |
| 783 | auto AccumulateReexports = [&](StringRef Path, const ArchitectureSet &Archs) { |
| 784 | auto ReexportIFOrErr = getInterfaceFile(Filename: Path); |
| 785 | if (!ReexportIFOrErr) |
| 786 | return false; |
| 787 | std::unique_ptr<InterfaceFile> Reexport = std::move(*ReexportIFOrErr); |
| 788 | StringRef InstallName = Reexport->getInstallName(); |
| 789 | assert(!InstallName.empty() && "Parse error for install name" ); |
| 790 | Reexports.getArchSet(Attr: InstallName) = Archs; |
| 791 | ReexportIFs.emplace_back(Args: std::move(*Reexport)); |
| 792 | return true; |
| 793 | }; |
| 794 | |
| 795 | PlatformSet Platforms; |
| 796 | for (const auto &T : DriverOpts.Targets) |
| 797 | Platforms.insert(V: T.first.Platform); |
| 798 | // Populate search paths by looking at user paths before system ones. |
| 799 | PathSeq FwkSearchPaths(FEOpts.FwkPaths.begin(), FEOpts.FwkPaths.end()); |
| 800 | for (const PlatformType P : Platforms) { |
| 801 | PathSeq PlatformSearchPaths = getPathsForPlatform(Paths: FEOpts.SystemFwkPaths, Platform: P); |
| 802 | llvm::append_range(C&: FwkSearchPaths, R&: PlatformSearchPaths); |
| 803 | for (const auto &[Lib, Archs] : LinkerOpts.ReexportedFrameworks.get()) { |
| 804 | std::string Name = (Lib + ".framework/" + Lib); |
| 805 | std::string Path = findLibrary(InstallName: Name, FM&: *FM, FrameworkSearchPaths: FwkSearchPaths, LibrarySearchPaths: {}, SearchPaths: {}); |
| 806 | if (Path.empty()) { |
| 807 | Diags->Report(DiagID: diag::err_cannot_find_reexport) << false << Lib; |
| 808 | return {}; |
| 809 | } |
| 810 | if (DriverOpts.TraceLibraryLocation) |
| 811 | errs() << Path << "\n" ; |
| 812 | |
| 813 | AccumulateReexports(Path, Archs); |
| 814 | } |
| 815 | FwkSearchPaths.resize(new_size: FwkSearchPaths.size() - PlatformSearchPaths.size()); |
| 816 | } |
| 817 | |
| 818 | for (const auto &[Lib, Archs] : LinkerOpts.ReexportedLibraries.get()) { |
| 819 | std::string Name = "lib" + Lib + ".dylib" ; |
| 820 | std::string Path = findLibrary(InstallName: Name, FM&: *FM, FrameworkSearchPaths: {}, LibrarySearchPaths: LinkerOpts.LibPaths, SearchPaths: {}); |
| 821 | if (Path.empty()) { |
| 822 | Diags->Report(DiagID: diag::err_cannot_find_reexport) << true << Lib; |
| 823 | return {}; |
| 824 | } |
| 825 | if (DriverOpts.TraceLibraryLocation) |
| 826 | errs() << Path << "\n" ; |
| 827 | |
| 828 | AccumulateReexports(Path, Archs); |
| 829 | } |
| 830 | |
| 831 | for (const auto &[Lib, Archs] : LinkerOpts.ReexportedLibraryPaths.get()) |
| 832 | AccumulateReexports(Lib, Archs); |
| 833 | |
| 834 | return {std::move(Reexports), std::move(ReexportIFs)}; |
| 835 | } |
| 836 | |
| 837 | InstallAPIContext Options::createContext() { |
| 838 | InstallAPIContext Ctx; |
| 839 | Ctx.FM = FM; |
| 840 | Ctx.Diags = Diags; |
| 841 | |
| 842 | // InstallAPI requires two level namespacing. |
| 843 | Ctx.BA.TwoLevelNamespace = true; |
| 844 | |
| 845 | Ctx.BA.InstallName = LinkerOpts.InstallName; |
| 846 | Ctx.BA.CurrentVersion = LinkerOpts.CurrentVersion; |
| 847 | Ctx.BA.CompatVersion = LinkerOpts.CompatVersion; |
| 848 | Ctx.BA.AppExtensionSafe = LinkerOpts.AppExtensionSafe; |
| 849 | Ctx.BA.ParentUmbrella = LinkerOpts.ParentUmbrella; |
| 850 | Ctx.BA.OSLibNotForSharedCache = LinkerOpts.OSLibNotForSharedCache; |
| 851 | Ctx.FT = DriverOpts.OutFT; |
| 852 | Ctx.OutputLoc = DriverOpts.OutputPath; |
| 853 | Ctx.LangMode = FEOpts.LangMode; |
| 854 | |
| 855 | auto [Reexports, ReexportedIFs] = getReexportedLibraries(); |
| 856 | if (Diags->hasErrorOccurred()) |
| 857 | return Ctx; |
| 858 | Ctx.Reexports = Reexports; |
| 859 | |
| 860 | // Collect symbols from alias lists. |
| 861 | AliasMap Aliases; |
| 862 | for (const StringRef ListPath : LinkerOpts.AliasLists) { |
| 863 | auto Buffer = FM->getBufferForFile(Filename: ListPath); |
| 864 | if (auto Err = Buffer.getError()) { |
| 865 | Diags->Report(DiagID: diag::err_cannot_open_file) << ListPath << Err.message(); |
| 866 | return Ctx; |
| 867 | } |
| 868 | Expected<AliasMap> Result = parseAliasList(Buffer&: Buffer.get()); |
| 869 | if (!Result) { |
| 870 | Diags->Report(DiagID: diag::err_cannot_read_input_list) |
| 871 | << "symbol alias" << ListPath << toString(E: Result.takeError()); |
| 872 | return Ctx; |
| 873 | } |
| 874 | Aliases.insert(first: Result.get().begin(), last: Result.get().end()); |
| 875 | } |
| 876 | |
| 877 | // Attempt to find umbrella headers by capturing framework name. |
| 878 | StringRef FrameworkName; |
| 879 | if (!LinkerOpts.IsDylib) |
| 880 | FrameworkName = |
| 881 | Library::getFrameworkNameFromInstallName(InstallName: LinkerOpts.InstallName); |
| 882 | |
| 883 | /// Process inputs headers. |
| 884 | // 1. For headers discovered by directory scanning, sort them. |
| 885 | // 2. For headers discovered by filelist, respect ordering. |
| 886 | // 3. Append extra headers and mark any excluded headers. |
| 887 | // 4. Finally, surface up umbrella headers to top of the list. |
| 888 | if (!DriverOpts.InputDirectory.empty()) { |
| 889 | DirectoryScanner Scanner(*FM, LinkerOpts.IsDylib |
| 890 | ? ScanMode::ScanDylibs |
| 891 | : ScanMode::ScanFrameworks); |
| 892 | SmallString<PATH_MAX> NormalizedPath(DriverOpts.InputDirectory); |
| 893 | FM->getVirtualFileSystem().makeAbsolute(Path&: NormalizedPath); |
| 894 | sys::path::remove_dots(path&: NormalizedPath, /*remove_dot_dot=*/true); |
| 895 | if (llvm::Error Err = Scanner.scan(Directory: NormalizedPath)) { |
| 896 | Diags->Report(DiagID: diag::err_directory_scanning) |
| 897 | << DriverOpts.InputDirectory << std::move(Err); |
| 898 | return Ctx; |
| 899 | } |
| 900 | std::vector<Library> InputLibraries = Scanner.takeLibraries(); |
| 901 | if (InputLibraries.size() > 1) { |
| 902 | Diags->Report(DiagID: diag::err_more_than_one_library); |
| 903 | return Ctx; |
| 904 | } |
| 905 | llvm::append_range(C&: Ctx.InputHeaders, |
| 906 | R: DirectoryScanner::getHeaders(Libraries: InputLibraries)); |
| 907 | llvm::stable_sort(Range&: Ctx.InputHeaders); |
| 908 | } |
| 909 | |
| 910 | for (const StringRef ListPath : DriverOpts.FileLists) { |
| 911 | auto Buffer = FM->getBufferForFile(Filename: ListPath); |
| 912 | if (auto Err = Buffer.getError()) { |
| 913 | Diags->Report(DiagID: diag::err_cannot_open_file) << ListPath << Err.message(); |
| 914 | return Ctx; |
| 915 | } |
| 916 | if (auto Err = FileListReader::loadHeaders(InputBuffer: std::move(Buffer.get()), |
| 917 | Destination&: Ctx.InputHeaders, FM)) { |
| 918 | Diags->Report(DiagID: diag::err_cannot_read_input_list) |
| 919 | << "header file" << ListPath << std::move(Err); |
| 920 | return Ctx; |
| 921 | } |
| 922 | } |
| 923 | // After initial input has been processed, add any extra headers. |
| 924 | auto HandleExtraHeaders = [&](PathSeq &, HeaderType Type) -> bool { |
| 925 | assert(Type != HeaderType::Unknown && "Missing header type." ); |
| 926 | for (const StringRef Path : Headers) { |
| 927 | if (!FM->getOptionalFileRef(Filename: Path)) { |
| 928 | Diags->Report(DiagID: diag::err_no_such_header_file) << Path << (unsigned)Type; |
| 929 | return false; |
| 930 | } |
| 931 | SmallString<PATH_MAX> FullPath(Path); |
| 932 | FM->makeAbsolutePath(Path&: FullPath); |
| 933 | |
| 934 | auto IncludeName = createIncludeHeaderName(FullPath); |
| 935 | Ctx.InputHeaders.emplace_back( |
| 936 | args&: FullPath, args&: Type, args: IncludeName.has_value() ? *IncludeName : "" ); |
| 937 | Ctx.InputHeaders.back().setExtra(); |
| 938 | } |
| 939 | return true; |
| 940 | }; |
| 941 | |
| 942 | if (!HandleExtraHeaders(DriverOpts.ExtraPublicHeaders, HeaderType::Public) || |
| 943 | !HandleExtraHeaders(DriverOpts.ExtraPrivateHeaders, |
| 944 | HeaderType::Private) || |
| 945 | !HandleExtraHeaders(DriverOpts.ExtraProjectHeaders, HeaderType::Project)) |
| 946 | return Ctx; |
| 947 | |
| 948 | // After all headers have been added, consider excluded headers. |
| 949 | std::vector<std::unique_ptr<HeaderGlob>> ; |
| 950 | std::set<FileEntryRef> ; |
| 951 | auto ParseGlobs = [&](const PathSeq &Paths, HeaderType Type) { |
| 952 | assert(Type != HeaderType::Unknown && "Missing header type." ); |
| 953 | for (const StringRef Path : Paths) { |
| 954 | auto Glob = HeaderGlob::create(GlobString: Path, Type); |
| 955 | if (Glob) |
| 956 | ExcludedHeaderGlobs.emplace_back(args: std::move(Glob.get())); |
| 957 | else { |
| 958 | consumeError(Err: Glob.takeError()); |
| 959 | if (auto File = FM->getFileRef(Filename: Path)) |
| 960 | ExcludedHeaderFiles.emplace(args&: *File); |
| 961 | else { |
| 962 | Diags->Report(DiagID: diag::err_no_such_header_file) |
| 963 | << Path << (unsigned)Type; |
| 964 | return false; |
| 965 | } |
| 966 | } |
| 967 | } |
| 968 | return true; |
| 969 | }; |
| 970 | |
| 971 | if (!ParseGlobs(DriverOpts.ExcludePublicHeaders, HeaderType::Public) || |
| 972 | !ParseGlobs(DriverOpts.ExcludePrivateHeaders, HeaderType::Private) || |
| 973 | !ParseGlobs(DriverOpts.ExcludeProjectHeaders, HeaderType::Project)) |
| 974 | return Ctx; |
| 975 | |
| 976 | for (HeaderFile & : Ctx.InputHeaders) { |
| 977 | for (auto &Glob : ExcludedHeaderGlobs) |
| 978 | if (Glob->match(Header)) |
| 979 | Header.setExcluded(); |
| 980 | } |
| 981 | if (!ExcludedHeaderFiles.empty()) { |
| 982 | for (HeaderFile & : Ctx.InputHeaders) { |
| 983 | auto FileRef = FM->getFileRef(Filename: Header.getPath()); |
| 984 | if (!FileRef) |
| 985 | continue; |
| 986 | if (ExcludedHeaderFiles.count(x: *FileRef)) |
| 987 | Header.setExcluded(); |
| 988 | } |
| 989 | } |
| 990 | // Report if glob was ignored. |
| 991 | for (const auto &Glob : ExcludedHeaderGlobs) |
| 992 | if (!Glob->didMatch()) |
| 993 | Diags->Report(DiagID: diag::warn_glob_did_not_match) << Glob->str(); |
| 994 | |
| 995 | // Mark any explicit or inferred umbrella headers. If one exists, move |
| 996 | // that to the beginning of the input headers. |
| 997 | auto MarkandMoveUmbrellaInHeaders = [&](llvm::Regex &Regex, |
| 998 | HeaderType Type) -> bool { |
| 999 | auto It = find_if(Range&: Ctx.InputHeaders, P: [&Regex, Type](const HeaderFile &H) { |
| 1000 | return (H.getType() == Type) && Regex.match(String: H.getPath()); |
| 1001 | }); |
| 1002 | |
| 1003 | if (It == Ctx.InputHeaders.end()) |
| 1004 | return false; |
| 1005 | It->setUmbrellaHeader(); |
| 1006 | |
| 1007 | // Because there can be an umbrella header per header type, |
| 1008 | // find the first non umbrella header to swap position with. |
| 1009 | auto BeginPos = find_if(Range&: Ctx.InputHeaders, P: [](const HeaderFile &H) { |
| 1010 | return !H.isUmbrellaHeader(); |
| 1011 | }); |
| 1012 | if (BeginPos != Ctx.InputHeaders.end() && BeginPos < It) |
| 1013 | std::swap(a&: *BeginPos, b&: *It); |
| 1014 | return true; |
| 1015 | }; |
| 1016 | |
| 1017 | auto = [&](StringRef , HeaderType Type) -> bool { |
| 1018 | assert(Type != HeaderType::Unknown && "Missing header type." ); |
| 1019 | if (!HeaderPath.empty()) { |
| 1020 | auto EscapedString = Regex::escape(String: HeaderPath); |
| 1021 | Regex UmbrellaRegex(EscapedString); |
| 1022 | if (!MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type)) { |
| 1023 | Diags->Report(DiagID: diag::err_no_such_umbrella_header_file) |
| 1024 | << HeaderPath << (unsigned)Type; |
| 1025 | return false; |
| 1026 | } |
| 1027 | } else if (!FrameworkName.empty() && (Type != HeaderType::Project)) { |
| 1028 | auto UmbrellaName = "/" + Regex::escape(String: FrameworkName); |
| 1029 | if (Type == HeaderType::Public) |
| 1030 | UmbrellaName += "\\.h" ; |
| 1031 | else |
| 1032 | UmbrellaName += "[_]?Private\\.h" ; |
| 1033 | Regex UmbrellaRegex(UmbrellaName); |
| 1034 | MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type); |
| 1035 | } |
| 1036 | return true; |
| 1037 | }; |
| 1038 | if (!FindUmbrellaHeader(DriverOpts.PublicUmbrellaHeader, |
| 1039 | HeaderType::Public) || |
| 1040 | !FindUmbrellaHeader(DriverOpts.PrivateUmbrellaHeader, |
| 1041 | HeaderType::Private) || |
| 1042 | !FindUmbrellaHeader(DriverOpts.ProjectUmbrellaHeader, |
| 1043 | HeaderType::Project)) |
| 1044 | return Ctx; |
| 1045 | |
| 1046 | // Parse binary dylib and initialize verifier. |
| 1047 | if (DriverOpts.DylibToVerify.empty()) { |
| 1048 | Ctx.Verifier = std::make_unique<DylibVerifier>(); |
| 1049 | return Ctx; |
| 1050 | } |
| 1051 | |
| 1052 | auto Buffer = FM->getBufferForFile(Filename: DriverOpts.DylibToVerify); |
| 1053 | if (auto Err = Buffer.getError()) { |
| 1054 | Diags->Report(DiagID: diag::err_cannot_open_file) |
| 1055 | << DriverOpts.DylibToVerify << Err.message(); |
| 1056 | return Ctx; |
| 1057 | } |
| 1058 | |
| 1059 | DylibReader::ParseOption PO; |
| 1060 | PO.Undefineds = false; |
| 1061 | Expected<Records> Slices = |
| 1062 | DylibReader::readFile(Buffer: (*Buffer)->getMemBufferRef(), Opt: PO); |
| 1063 | if (auto Err = Slices.takeError()) { |
| 1064 | Diags->Report(DiagID: diag::err_cannot_open_file) |
| 1065 | << DriverOpts.DylibToVerify << std::move(Err); |
| 1066 | return Ctx; |
| 1067 | } |
| 1068 | |
| 1069 | Ctx.Verifier = std::make_unique<DylibVerifier>( |
| 1070 | args: std::move(*Slices), args: std::move(ReexportedIFs), args: std::move(Aliases), args&: Diags, |
| 1071 | args&: DriverOpts.VerifyMode, args&: DriverOpts.Zippered, args&: DriverOpts.Demangle, |
| 1072 | args&: DriverOpts.DSYMPath); |
| 1073 | return Ctx; |
| 1074 | } |
| 1075 | |
| 1076 | void Options::(std::vector<std::string> &ArgStrings, |
| 1077 | const llvm::Triple &Targ, |
| 1078 | const HeaderType Type) { |
| 1079 | // Unique to architecture (Xarch) options hold no arguments to pass along for |
| 1080 | // frontend. |
| 1081 | |
| 1082 | // Add specific to platform arguments. |
| 1083 | PathSeq PlatformSearchPaths = |
| 1084 | getPathsForPlatform(Paths: FEOpts.SystemFwkPaths, Platform: mapToPlatformType(Target: Targ)); |
| 1085 | for (StringRef Path : PlatformSearchPaths) { |
| 1086 | ArgStrings.push_back(x: "-iframework" ); |
| 1087 | ArgStrings.push_back(x: Path.str()); |
| 1088 | } |
| 1089 | |
| 1090 | // Add specific to header type arguments. |
| 1091 | if (Type == HeaderType::Project) |
| 1092 | for (const StringRef A : ProjectLevelArgs) |
| 1093 | ArgStrings.emplace_back(args: A); |
| 1094 | } |
| 1095 | |
| 1096 | } // namespace installapi |
| 1097 | } // namespace clang |
| 1098 | |