|  | //===-- Options.cpp -------------------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Options.h" | 
|  | #include "clang/Basic/DiagnosticIDs.h" | 
|  | #include "clang/Driver/Driver.h" | 
|  | #include "clang/InstallAPI/DirectoryScanner.h" | 
|  | #include "clang/InstallAPI/FileList.h" | 
|  | #include "clang/InstallAPI/HeaderFile.h" | 
|  | #include "clang/InstallAPI/InstallAPIDiagnostic.h" | 
|  | #include "llvm/BinaryFormat/Magic.h" | 
|  | #include "llvm/Support/JSON.h" | 
|  | #include "llvm/Support/Program.h" | 
|  | #include "llvm/TargetParser/Host.h" | 
|  | #include "llvm/TextAPI/DylibReader.h" | 
|  | #include "llvm/TextAPI/TextAPIError.h" | 
|  | #include "llvm/TextAPI/TextAPIReader.h" | 
|  | #include "llvm/TextAPI/TextAPIWriter.h" | 
|  |  | 
|  | using namespace llvm; | 
|  | using namespace llvm::opt; | 
|  | using namespace llvm::MachO; | 
|  |  | 
|  | namespace drv = clang::driver::options; | 
|  |  | 
|  | namespace clang { | 
|  | namespace installapi { | 
|  |  | 
|  | #define OPTTABLE_STR_TABLE_CODE | 
|  | #include "InstallAPIOpts.inc" | 
|  | #undef OPTTABLE_STR_TABLE_CODE | 
|  |  | 
|  | #define OPTTABLE_PREFIXES_TABLE_CODE | 
|  | #include "InstallAPIOpts.inc" | 
|  | #undef OPTTABLE_PREFIXES_TABLE_CODE | 
|  |  | 
|  | #define OPTTABLE_PREFIXES_UNION_CODE | 
|  | #include "InstallAPIOpts.inc" | 
|  | #undef OPTTABLE_PREFIXES_UNION_CODE | 
|  |  | 
|  | /// Create table mapping all options defined in InstallAPIOpts.td. | 
|  | static constexpr OptTable::Info InfoTable[] = { | 
|  | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), | 
|  | #include "InstallAPIOpts.inc" | 
|  | #undef OPTION | 
|  | }; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | /// \brief Create OptTable class for parsing actual command line arguments. | 
|  | class DriverOptTable : public opt::PrecomputedOptTable { | 
|  | public: | 
|  | DriverOptTable() | 
|  | : PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, | 
|  | OptionPrefixesUnion) {} | 
|  | }; | 
|  |  | 
|  | } // end anonymous namespace. | 
|  |  | 
|  | static llvm::opt::OptTable *createDriverOptTable() { | 
|  | return new DriverOptTable(); | 
|  | } | 
|  |  | 
|  | /// Parse JSON input into argument list. | 
|  | /// | 
|  | /* Expected input format. | 
|  | *  { "label" : ["-ClangArg1", "-ClangArg2"] } | 
|  | */ | 
|  | /// | 
|  | /// Input is interpreted as "-Xlabel ClangArg1 -XLabel ClangArg2". | 
|  | static Expected<llvm::opt::InputArgList> | 
|  | getArgListFromJSON(const StringRef Input, llvm::opt::OptTable *Table, | 
|  | std::vector<std::string> &Storage) { | 
|  | using namespace json; | 
|  | Expected<Value> ValOrErr = json::parse(Input); | 
|  | if (!ValOrErr) | 
|  | return ValOrErr.takeError(); | 
|  |  | 
|  | const Object *Root = ValOrErr->getAsObject(); | 
|  | if (!Root) | 
|  | return llvm::opt::InputArgList(); | 
|  |  | 
|  | for (const auto &KV : *Root) { | 
|  | const Array *ArgList = KV.getSecond().getAsArray(); | 
|  | std::string Label = "-X" + KV.getFirst().str(); | 
|  | if (!ArgList) | 
|  | return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat); | 
|  | for (auto Arg : *ArgList) { | 
|  | std::optional<StringRef> ArgStr = Arg.getAsString(); | 
|  | if (!ArgStr) | 
|  | return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat); | 
|  | Storage.emplace_back(Label); | 
|  | Storage.emplace_back(*ArgStr); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<const char *> CArgs(Storage.size()); | 
|  | llvm::for_each(Storage, | 
|  | [&CArgs](StringRef Str) { CArgs.emplace_back(Str.data()); }); | 
|  |  | 
|  | unsigned MissingArgIndex, MissingArgCount; | 
|  | return Table->ParseArgs(CArgs, MissingArgIndex, MissingArgCount); | 
|  | } | 
|  |  | 
|  | bool Options::processDriverOptions(InputArgList &Args) { | 
|  | // Handle inputs. | 
|  | for (const StringRef Path : Args.getAllArgValues(drv::OPT_INPUT)) { | 
|  | // Assume any input that is not a directory is a filelist. | 
|  | // InstallAPI does not accept multiple directories, so retain the last one. | 
|  | if (FM->getOptionalDirectoryRef(Path)) | 
|  | DriverOpts.InputDirectory = Path.str(); | 
|  | else | 
|  | DriverOpts.FileLists.emplace_back(Path.str()); | 
|  | } | 
|  |  | 
|  | // Handle output. | 
|  | SmallString<PATH_MAX> OutputPath; | 
|  | if (auto *Arg = Args.getLastArg(drv::OPT_o)) { | 
|  | OutputPath = Arg->getValue(); | 
|  | if (OutputPath != "-") | 
|  | FM->makeAbsolutePath(OutputPath); | 
|  | DriverOpts.OutputPath = std::string(OutputPath); | 
|  | } | 
|  | if (DriverOpts.OutputPath.empty()) { | 
|  | Diags->Report(diag::err_no_output_file); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Do basic error checking first for mixing -target and -arch options. | 
|  | auto *ArgArch = Args.getLastArgNoClaim(drv::OPT_arch); | 
|  | auto *ArgTarget = Args.getLastArgNoClaim(drv::OPT_target); | 
|  | auto *ArgTargetVariant = | 
|  | Args.getLastArgNoClaim(drv::OPT_darwin_target_variant); | 
|  | if (ArgArch && (ArgTarget || ArgTargetVariant)) { | 
|  | Diags->Report(clang::diag::err_drv_argument_not_allowed_with) | 
|  | << ArgArch->getAsString(Args) | 
|  | << (ArgTarget ? ArgTarget : ArgTargetVariant)->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | auto *ArgMinTargetOS = Args.getLastArgNoClaim(drv::OPT_mtargetos_EQ); | 
|  | if ((ArgTarget || ArgTargetVariant) && ArgMinTargetOS) { | 
|  | Diags->Report(clang::diag::err_drv_cannot_mix_options) | 
|  | << ArgTarget->getAsString(Args) << ArgMinTargetOS->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Capture target triples first. | 
|  | if (ArgTarget) { | 
|  | for (const Arg *A : Args.filtered(drv::OPT_target)) { | 
|  | A->claim(); | 
|  | llvm::Triple TargetTriple(A->getValue()); | 
|  | Target TAPITarget = Target(TargetTriple); | 
|  | if ((TAPITarget.Arch == AK_unknown) || | 
|  | (TAPITarget.Platform == PLATFORM_UNKNOWN)) { | 
|  | Diags->Report(clang::diag::err_drv_unsupported_opt_for_target) | 
|  | << "installapi" << TargetTriple.str(); | 
|  | return false; | 
|  | } | 
|  | DriverOpts.Targets[TAPITarget] = TargetTriple; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Capture target variants. | 
|  | DriverOpts.Zippered = ArgTargetVariant != nullptr; | 
|  | for (Arg *A : Args.filtered(drv::OPT_darwin_target_variant)) { | 
|  | A->claim(); | 
|  | Triple Variant(A->getValue()); | 
|  | if (Variant.getVendor() != Triple::Apple) { | 
|  | Diags->Report(diag::err_unsupported_vendor) | 
|  | << Variant.getVendorName() << A->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | switch (Variant.getOS()) { | 
|  | default: | 
|  | Diags->Report(diag::err_unsupported_os) | 
|  | << Variant.getOSName() << A->getAsString(Args); | 
|  | return false; | 
|  | case Triple::MacOSX: | 
|  | case Triple::IOS: | 
|  | break; | 
|  | } | 
|  |  | 
|  | switch (Variant.getEnvironment()) { | 
|  | default: | 
|  | Diags->Report(diag::err_unsupported_environment) | 
|  | << Variant.getEnvironmentName() << A->getAsString(Args); | 
|  | return false; | 
|  | case Triple::UnknownEnvironment: | 
|  | case Triple::MacABI: | 
|  | break; | 
|  | } | 
|  |  | 
|  | Target TAPIVariant(Variant); | 
|  | // See if there is a matching --target option for this --target-variant | 
|  | // option. | 
|  | auto It = find_if(DriverOpts.Targets, [&](const auto &T) { | 
|  | return (T.first.Arch == TAPIVariant.Arch) && | 
|  | (T.first.Platform != PlatformType::PLATFORM_UNKNOWN); | 
|  | }); | 
|  |  | 
|  | if (It == DriverOpts.Targets.end()) { | 
|  | Diags->Report(diag::err_no_matching_target) << Variant.str(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | DriverOpts.Targets[TAPIVariant] = Variant; | 
|  | } | 
|  |  | 
|  | DriverOpts.Verbose = Args.hasArgNoClaim(drv::OPT_v); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Options::processInstallAPIXOptions(InputArgList &Args) { | 
|  | for (arg_iterator It = Args.begin(), End = Args.end(); It != End; ++It) { | 
|  | Arg *A = *It; | 
|  | if (A->getOption().matches(OPT_Xarch__)) { | 
|  | if (!processXarchOption(Args, It)) | 
|  | return false; | 
|  | continue; | 
|  | } else if (A->getOption().matches(OPT_Xplatform__)) { | 
|  | if (!processXplatformOption(Args, It)) | 
|  | return false; | 
|  | continue; | 
|  | } else if (A->getOption().matches(OPT_Xproject)) { | 
|  | if (!processXprojectOption(Args, It)) | 
|  | return false; | 
|  | continue; | 
|  | } else if (!A->getOption().matches(OPT_X__)) | 
|  | continue; | 
|  |  | 
|  | // Handle any user defined labels. | 
|  | const StringRef Label = A->getValue(0); | 
|  |  | 
|  | // Ban "public" and "private" labels. | 
|  | if ((Label.lower() == "public") || (Label.lower() == "private")) { | 
|  | Diags->Report(diag::err_invalid_label) << Label; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | auto NextIt = std::next(It); | 
|  | if (NextIt == End) { | 
|  | Diags->Report(clang::diag::err_drv_missing_argument) | 
|  | << A->getAsString(Args) << 1; | 
|  | return false; | 
|  | } | 
|  | Arg *NextA = *NextIt; | 
|  | switch ((ID)NextA->getOption().getID()) { | 
|  | case OPT_D: | 
|  | case OPT_U: | 
|  | break; | 
|  | default: | 
|  | Diags->Report(clang::diag::err_drv_argument_not_allowed_with) | 
|  | << A->getAsString(Args) << NextA->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  | const StringRef ASpelling = NextA->getSpelling(); | 
|  | const auto &AValues = NextA->getValues(); | 
|  | if (AValues.empty()) | 
|  | FEOpts.UniqueArgs[Label].emplace_back(ASpelling.str()); | 
|  | else | 
|  | for (const StringRef Val : AValues) | 
|  | FEOpts.UniqueArgs[Label].emplace_back((ASpelling + Val).str()); | 
|  |  | 
|  | A->claim(); | 
|  | NextA->claim(); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Options::processXplatformOption(InputArgList &Args, arg_iterator Curr) { | 
|  | Arg *A = *Curr; | 
|  |  | 
|  | PlatformType Platform = getPlatformFromName(A->getValue(0)); | 
|  | if (Platform == PLATFORM_UNKNOWN) { | 
|  | Diags->Report(diag::err_unsupported_os) | 
|  | << getPlatformName(Platform) << A->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  | auto NextIt = std::next(Curr); | 
|  | if (NextIt == Args.end()) { | 
|  | Diags->Report(diag::err_drv_missing_argument) << A->getAsString(Args) << 1; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Arg *NextA = *NextIt; | 
|  | switch ((ID)NextA->getOption().getID()) { | 
|  | case OPT_iframework: | 
|  | FEOpts.SystemFwkPaths.emplace_back(NextA->getValue(), Platform); | 
|  | break; | 
|  | default: | 
|  | Diags->Report(diag::err_drv_invalid_argument_to_option) | 
|  | << A->getAsString(Args) << NextA->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | A->claim(); | 
|  | NextA->claim(); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Options::processXprojectOption(InputArgList &Args, arg_iterator Curr) { | 
|  | Arg *A = *Curr; | 
|  | auto NextIt = std::next(Curr); | 
|  | if (NextIt == Args.end()) { | 
|  | Diags->Report(diag::err_drv_missing_argument) << A->getAsString(Args) << 1; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Arg *NextA = *NextIt; | 
|  | switch ((ID)NextA->getOption().getID()) { | 
|  | case OPT_fobjc_arc: | 
|  | case OPT_fmodules: | 
|  | case OPT_fmodules_cache_path: | 
|  | case OPT_include_: | 
|  | case OPT_fvisibility_EQ: | 
|  | break; | 
|  | default: | 
|  | Diags->Report(diag::err_drv_argument_not_allowed_with) | 
|  | << A->getAsString(Args) << NextA->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::string ArgString = NextA->getSpelling().str(); | 
|  | for (const StringRef Val : NextA->getValues()) | 
|  | ArgString += Val.str(); | 
|  |  | 
|  | ProjectLevelArgs.push_back(ArgString); | 
|  | A->claim(); | 
|  | NextA->claim(); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Options::processXarchOption(InputArgList &Args, arg_iterator Curr) { | 
|  | Arg *CurrArg = *Curr; | 
|  | Architecture Arch = getArchitectureFromName(CurrArg->getValue(0)); | 
|  | if (Arch == AK_unknown) { | 
|  | Diags->Report(diag::err_drv_invalid_arch_name) | 
|  | << CurrArg->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | auto NextIt = std::next(Curr); | 
|  | if (NextIt == Args.end()) { | 
|  | Diags->Report(diag::err_drv_missing_argument) | 
|  | << CurrArg->getAsString(Args) << 1; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // InstallAPI has a limited understanding of supported Xarch options. | 
|  | // Currently this is restricted to linker inputs. | 
|  | const Arg *NextArg = *NextIt; | 
|  | switch (NextArg->getOption().getID()) { | 
|  | case OPT_allowable_client: | 
|  | case OPT_reexport_l: | 
|  | case OPT_reexport_framework: | 
|  | case OPT_reexport_library: | 
|  | case OPT_rpath: | 
|  | break; | 
|  | default: | 
|  | Diags->Report(diag::err_drv_invalid_argument_to_option) | 
|  | << NextArg->getAsString(Args) << CurrArg->getAsString(Args); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ArgToArchMap[NextArg] = Arch; | 
|  | CurrArg->claim(); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Options::processOptionList(InputArgList &Args, | 
|  | llvm::opt::OptTable *Table) { | 
|  | Arg *A = Args.getLastArg(OPT_option_list); | 
|  | if (!A) | 
|  | return true; | 
|  |  | 
|  | const StringRef Path = A->getValue(0); | 
|  | auto InputOrErr = FM->getBufferForFile(Path); | 
|  | if (auto Err = InputOrErr.getError()) { | 
|  | Diags->Report(diag::err_cannot_open_file) << Path << Err.message(); | 
|  | return false; | 
|  | } | 
|  | // Backing storage referenced for argument processing. | 
|  | std::vector<std::string> Storage; | 
|  | auto ArgsOrErr = | 
|  | getArgListFromJSON((*InputOrErr)->getBuffer(), Table, Storage); | 
|  |  | 
|  | if (auto Err = ArgsOrErr.takeError()) { | 
|  | Diags->Report(diag::err_cannot_read_input_list) | 
|  | << "option" << Path << toString(std::move(Err)); | 
|  | return false; | 
|  | } | 
|  | return processInstallAPIXOptions(*ArgsOrErr); | 
|  | } | 
|  |  | 
|  | bool Options::processLinkerOptions(InputArgList &Args) { | 
|  | // Handle required arguments. | 
|  | if (const Arg *A = Args.getLastArg(drv::OPT_install__name)) | 
|  | LinkerOpts.InstallName = A->getValue(); | 
|  | if (LinkerOpts.InstallName.empty()) { | 
|  | Diags->Report(diag::err_no_install_name); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Defaulted or optional arguments. | 
|  | if (auto *Arg = Args.getLastArg(drv::OPT_current__version)) | 
|  | LinkerOpts.CurrentVersion.parse64(Arg->getValue()); | 
|  |  | 
|  | if (auto *Arg = Args.getLastArg(drv::OPT_compatibility__version)) | 
|  | LinkerOpts.CompatVersion.parse64(Arg->getValue()); | 
|  |  | 
|  | if (auto *Arg = Args.getLastArg(drv::OPT_compatibility__version)) | 
|  | LinkerOpts.CompatVersion.parse64(Arg->getValue()); | 
|  |  | 
|  | if (auto *Arg = Args.getLastArg(drv::OPT_umbrella)) | 
|  | LinkerOpts.ParentUmbrella = Arg->getValue(); | 
|  |  | 
|  | LinkerOpts.IsDylib = Args.hasArg(drv::OPT_dynamiclib); | 
|  |  | 
|  | for (auto *Arg : Args.filtered(drv::OPT_alias_list)) { | 
|  | LinkerOpts.AliasLists.emplace_back(Arg->getValue()); | 
|  | Arg->claim(); | 
|  | } | 
|  |  | 
|  | LinkerOpts.AppExtensionSafe = Args.hasFlag( | 
|  | drv::OPT_fapplication_extension, drv::OPT_fno_application_extension, | 
|  | /*Default=*/LinkerOpts.AppExtensionSafe); | 
|  |  | 
|  | if (::getenv("LD_NO_ENCRYPT") != nullptr) | 
|  | LinkerOpts.AppExtensionSafe = true; | 
|  |  | 
|  | if (::getenv("LD_APPLICATION_EXTENSION_SAFE") != nullptr) | 
|  | LinkerOpts.AppExtensionSafe = true; | 
|  |  | 
|  | // Capture library paths. | 
|  | PathSeq LibraryPaths; | 
|  | for (const Arg *A : Args.filtered(drv::OPT_L)) { | 
|  | LibraryPaths.emplace_back(A->getValue()); | 
|  | A->claim(); | 
|  | } | 
|  |  | 
|  | if (!LibraryPaths.empty()) | 
|  | LinkerOpts.LibPaths = std::move(LibraryPaths); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // NOTE: Do not claim any arguments, as they will be passed along for CC1 | 
|  | // invocations. | 
|  | bool Options::processFrontendOptions(InputArgList &Args) { | 
|  | // Capture language mode. | 
|  | if (auto *A = Args.getLastArgNoClaim(drv::OPT_x)) { | 
|  | FEOpts.LangMode = llvm::StringSwitch<clang::Language>(A->getValue()) | 
|  | .Case("c", clang::Language::C) | 
|  | .Case("c++", clang::Language::CXX) | 
|  | .Case("objective-c", clang::Language::ObjC) | 
|  | .Case("objective-c++", clang::Language::ObjCXX) | 
|  | .Default(clang::Language::Unknown); | 
|  |  | 
|  | if (FEOpts.LangMode == clang::Language::Unknown) { | 
|  | Diags->Report(clang::diag::err_drv_invalid_value) | 
|  | << A->getAsString(Args) << A->getValue(); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | for (auto *A : Args.filtered(drv::OPT_ObjC, drv::OPT_ObjCXX)) { | 
|  | if (A->getOption().matches(drv::OPT_ObjC)) | 
|  | FEOpts.LangMode = clang::Language::ObjC; | 
|  | else | 
|  | FEOpts.LangMode = clang::Language::ObjCXX; | 
|  | } | 
|  |  | 
|  | // Capture Sysroot. | 
|  | if (const Arg *A = Args.getLastArgNoClaim(drv::OPT_isysroot)) { | 
|  | SmallString<PATH_MAX> Path(A->getValue()); | 
|  | FM->makeAbsolutePath(Path); | 
|  | if (!FM->getOptionalDirectoryRef(Path)) { | 
|  | Diags->Report(diag::err_missing_sysroot) << Path; | 
|  | return false; | 
|  | } | 
|  | FEOpts.ISysroot = std::string(Path); | 
|  | } else if (FEOpts.ISysroot.empty()) { | 
|  | // Mirror CLANG and obtain the isysroot from the SDKROOT environment | 
|  | // variable, if it wasn't defined by the  command line. | 
|  | if (auto *Env = ::getenv("SDKROOT")) { | 
|  | if (StringRef(Env) != "/" && llvm::sys::path::is_absolute(Env) && | 
|  | FM->getOptionalFileRef(Env)) | 
|  | FEOpts.ISysroot = Env; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Capture system frameworks for all platforms. | 
|  | for (const Arg *A : Args.filtered(drv::OPT_iframework)) | 
|  | FEOpts.SystemFwkPaths.emplace_back(A->getValue(), | 
|  | std::optional<PlatformType>{}); | 
|  |  | 
|  | // Capture framework paths. | 
|  | PathSeq FrameworkPaths; | 
|  | for (const Arg *A : Args.filtered(drv::OPT_F)) | 
|  | FrameworkPaths.emplace_back(A->getValue()); | 
|  |  | 
|  | if (!FrameworkPaths.empty()) | 
|  | FEOpts.FwkPaths = std::move(FrameworkPaths); | 
|  |  | 
|  | // Add default framework/library paths. | 
|  | PathSeq DefaultLibraryPaths = {"/usr/lib", "/usr/local/lib"}; | 
|  | PathSeq DefaultFrameworkPaths = {"/Library/Frameworks", | 
|  | "/System/Library/Frameworks"}; | 
|  |  | 
|  | for (const StringRef LibPath : DefaultLibraryPaths) { | 
|  | SmallString<PATH_MAX> Path(FEOpts.ISysroot); | 
|  | sys::path::append(Path, LibPath); | 
|  | LinkerOpts.LibPaths.emplace_back(Path.str()); | 
|  | } | 
|  | for (const StringRef FwkPath : DefaultFrameworkPaths) { | 
|  | SmallString<PATH_MAX> Path(FEOpts.ISysroot); | 
|  | sys::path::append(Path, FwkPath); | 
|  | FEOpts.SystemFwkPaths.emplace_back(Path.str(), | 
|  | std::optional<PlatformType>{}); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Options::addFilePaths(InputArgList &Args, PathSeq &Headers, | 
|  | OptSpecifier ID) { | 
|  | for (const StringRef Path : Args.getAllArgValues(ID)) { | 
|  | if ((bool)FM->getOptionalDirectoryRef(Path, /*CacheFailure=*/false)) { | 
|  | auto InputHeadersOrErr = enumerateFiles(*FM, Path); | 
|  | if (!InputHeadersOrErr) { | 
|  | Diags->Report(diag::err_cannot_open_file) | 
|  | << Path << toString(InputHeadersOrErr.takeError()); | 
|  | return false; | 
|  | } | 
|  | // Sort headers to ensure deterministic behavior. | 
|  | sort(*InputHeadersOrErr); | 
|  | for (StringRef H : *InputHeadersOrErr) | 
|  | Headers.emplace_back(std::move(H)); | 
|  | } else | 
|  | Headers.emplace_back(Path); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::vector<const char *> | 
|  | Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) { | 
|  | std::unique_ptr<llvm::opt::OptTable> Table; | 
|  | Table.reset(createDriverOptTable()); | 
|  |  | 
|  | unsigned MissingArgIndex, MissingArgCount; | 
|  | auto ParsedArgs = Table->ParseArgs(Args.slice(1), MissingArgIndex, | 
|  | MissingArgCount, Visibility()); | 
|  |  | 
|  | // Capture InstallAPI only driver options. | 
|  | if (!processInstallAPIXOptions(ParsedArgs)) | 
|  | return {}; | 
|  |  | 
|  | if (!processOptionList(ParsedArgs, Table.get())) | 
|  | return {}; | 
|  |  | 
|  | DriverOpts.Demangle = ParsedArgs.hasArg(OPT_demangle); | 
|  |  | 
|  | if (auto *A = ParsedArgs.getLastArg(OPT_filetype)) { | 
|  | DriverOpts.OutFT = TextAPIWriter::parseFileType(A->getValue()); | 
|  | if (DriverOpts.OutFT == FileType::Invalid) { | 
|  | Diags->Report(clang::diag::err_drv_invalid_value) | 
|  | << A->getAsString(ParsedArgs) << A->getValue(); | 
|  | return {}; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (const Arg *A = ParsedArgs.getLastArg(OPT_verify_mode_EQ)) { | 
|  | DriverOpts.VerifyMode = | 
|  | StringSwitch<VerificationMode>(A->getValue()) | 
|  | .Case("ErrorsOnly", VerificationMode::ErrorsOnly) | 
|  | .Case("ErrorsAndWarnings", VerificationMode::ErrorsAndWarnings) | 
|  | .Case("Pedantic", VerificationMode::Pedantic) | 
|  | .Default(VerificationMode::Invalid); | 
|  |  | 
|  | if (DriverOpts.VerifyMode == VerificationMode::Invalid) { | 
|  | Diags->Report(clang::diag::err_drv_invalid_value) | 
|  | << A->getAsString(ParsedArgs) << A->getValue(); | 
|  | return {}; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (const Arg *A = ParsedArgs.getLastArg(OPT_verify_against)) | 
|  | DriverOpts.DylibToVerify = A->getValue(); | 
|  |  | 
|  | if (const Arg *A = ParsedArgs.getLastArg(OPT_dsym)) | 
|  | DriverOpts.DSYMPath = A->getValue(); | 
|  |  | 
|  | DriverOpts.TraceLibraryLocation = ParsedArgs.hasArg(OPT_t); | 
|  |  | 
|  | // Linker options not handled by clang driver. | 
|  | LinkerOpts.OSLibNotForSharedCache = | 
|  | ParsedArgs.hasArg(OPT_not_for_dyld_shared_cache); | 
|  |  | 
|  | for (const Arg *A : ParsedArgs.filtered(OPT_allowable_client)) { | 
|  | LinkerOpts.AllowableClients[A->getValue()] = | 
|  | ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet(); | 
|  | A->claim(); | 
|  | } | 
|  |  | 
|  | for (const Arg *A : ParsedArgs.filtered(OPT_reexport_l)) { | 
|  | LinkerOpts.ReexportedLibraries[A->getValue()] = | 
|  | ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet(); | 
|  | A->claim(); | 
|  | } | 
|  |  | 
|  | for (const Arg *A : ParsedArgs.filtered(OPT_reexport_library)) { | 
|  | LinkerOpts.ReexportedLibraryPaths[A->getValue()] = | 
|  | ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet(); | 
|  | A->claim(); | 
|  | } | 
|  |  | 
|  | for (const Arg *A : ParsedArgs.filtered(OPT_reexport_framework)) { | 
|  | LinkerOpts.ReexportedFrameworks[A->getValue()] = | 
|  | ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet(); | 
|  | A->claim(); | 
|  | } | 
|  |  | 
|  | for (const Arg *A : ParsedArgs.filtered(OPT_rpath)) { | 
|  | LinkerOpts.RPaths[A->getValue()] = | 
|  | ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet(); | 
|  | A->claim(); | 
|  | } | 
|  |  | 
|  | // Handle exclude & extra header directories or files. | 
|  | auto handleAdditionalInputArgs = [&](PathSeq &Headers, | 
|  | clang::installapi::ID OptID) { | 
|  | if (ParsedArgs.hasArgNoClaim(OptID)) | 
|  | Headers.clear(); | 
|  | return addFilePaths(ParsedArgs, Headers, OptID); | 
|  | }; | 
|  |  | 
|  | if (!handleAdditionalInputArgs(DriverOpts.ExtraPublicHeaders, | 
|  | OPT_extra_public_header)) | 
|  | return {}; | 
|  |  | 
|  | if (!handleAdditionalInputArgs(DriverOpts.ExtraPrivateHeaders, | 
|  | OPT_extra_private_header)) | 
|  | return {}; | 
|  | if (!handleAdditionalInputArgs(DriverOpts.ExtraProjectHeaders, | 
|  | OPT_extra_project_header)) | 
|  | return {}; | 
|  |  | 
|  | if (!handleAdditionalInputArgs(DriverOpts.ExcludePublicHeaders, | 
|  | OPT_exclude_public_header)) | 
|  | return {}; | 
|  | if (!handleAdditionalInputArgs(DriverOpts.ExcludePrivateHeaders, | 
|  | OPT_exclude_private_header)) | 
|  | return {}; | 
|  | if (!handleAdditionalInputArgs(DriverOpts.ExcludeProjectHeaders, | 
|  | OPT_exclude_project_header)) | 
|  | return {}; | 
|  |  | 
|  | // Handle umbrella headers. | 
|  | if (const Arg *A = ParsedArgs.getLastArg(OPT_public_umbrella_header)) | 
|  | DriverOpts.PublicUmbrellaHeader = A->getValue(); | 
|  |  | 
|  | if (const Arg *A = ParsedArgs.getLastArg(OPT_private_umbrella_header)) | 
|  | DriverOpts.PrivateUmbrellaHeader = A->getValue(); | 
|  |  | 
|  | if (const Arg *A = ParsedArgs.getLastArg(OPT_project_umbrella_header)) | 
|  | DriverOpts.ProjectUmbrellaHeader = A->getValue(); | 
|  |  | 
|  | /// Any unclaimed arguments should be forwarded to the clang driver. | 
|  | std::vector<const char *> ClangDriverArgs(ParsedArgs.size()); | 
|  | for (const Arg *A : ParsedArgs) { | 
|  | if (A->isClaimed()) | 
|  | continue; | 
|  | // Forward along unclaimed but overlapping arguments to the clang driver. | 
|  | if (A->getOption().getID() > (unsigned)OPT_UNKNOWN) { | 
|  | ClangDriverArgs.push_back(A->getSpelling().data()); | 
|  | } else | 
|  | llvm::copy(A->getValues(), std::back_inserter(ClangDriverArgs)); | 
|  | } | 
|  | return ClangDriverArgs; | 
|  | } | 
|  |  | 
|  | Options::Options(DiagnosticsEngine &Diag, FileManager *FM, | 
|  | ArrayRef<const char *> Args, const StringRef ProgName) | 
|  | : Diags(&Diag), FM(FM) { | 
|  |  | 
|  | // First process InstallAPI specific options. | 
|  | auto DriverArgs = processAndFilterOutInstallAPIOptions(Args); | 
|  | if (Diags->hasErrorOccurred()) | 
|  | return; | 
|  |  | 
|  | // Set up driver to parse remaining input arguments. | 
|  | clang::driver::Driver Driver(ProgName, llvm::sys::getDefaultTargetTriple(), | 
|  | *Diags, "clang installapi tool"); | 
|  | auto TargetAndMode = | 
|  | clang::driver::ToolChain::getTargetAndModeFromProgramName(ProgName); | 
|  | Driver.setTargetAndMode(TargetAndMode); | 
|  | bool HasError = false; | 
|  | llvm::opt::InputArgList ArgList = | 
|  | Driver.ParseArgStrings(DriverArgs, /*UseDriverMode=*/true, HasError); | 
|  | if (HasError) | 
|  | return; | 
|  | Driver.setCheckInputsExist(false); | 
|  |  | 
|  | if (!processDriverOptions(ArgList)) | 
|  | return; | 
|  |  | 
|  | if (!processLinkerOptions(ArgList)) | 
|  | return; | 
|  |  | 
|  | if (!processFrontendOptions(ArgList)) | 
|  | return; | 
|  |  | 
|  | // After all InstallAPI necessary arguments have been collected. Go back and | 
|  | // assign values that were unknown before the clang driver opt table was used. | 
|  | ArchitectureSet AllArchs; | 
|  | llvm::for_each(DriverOpts.Targets, | 
|  | [&AllArchs](const auto &T) { AllArchs.set(T.first.Arch); }); | 
|  | auto assignDefaultLibAttrs = [&AllArchs](LibAttrs &Attrs) { | 
|  | for (StringMapEntry<ArchitectureSet> &Entry : Attrs) | 
|  | if (Entry.getValue().empty()) | 
|  | Entry.setValue(AllArchs); | 
|  | }; | 
|  | assignDefaultLibAttrs(LinkerOpts.AllowableClients); | 
|  | assignDefaultLibAttrs(LinkerOpts.ReexportedFrameworks); | 
|  | assignDefaultLibAttrs(LinkerOpts.ReexportedLibraries); | 
|  | assignDefaultLibAttrs(LinkerOpts.ReexportedLibraryPaths); | 
|  | assignDefaultLibAttrs(LinkerOpts.RPaths); | 
|  |  | 
|  | /// Force cc1 options that should always be on. | 
|  | FrontendArgs = {"-fsyntax-only", "-Wprivate-extern"}; | 
|  |  | 
|  | /// Any unclaimed arguments should be handled by invoking the clang frontend. | 
|  | for (const Arg *A : ArgList) { | 
|  | if (A->isClaimed()) | 
|  | continue; | 
|  | FrontendArgs.emplace_back(A->getSpelling()); | 
|  | llvm::copy(A->getValues(), std::back_inserter(FrontendArgs)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static Expected<std::unique_ptr<InterfaceFile>> | 
|  | getInterfaceFile(const StringRef Filename) { | 
|  | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = | 
|  | MemoryBuffer::getFile(Filename); | 
|  | if (auto Err = BufferOrErr.getError()) | 
|  | return errorCodeToError(std::move(Err)); | 
|  |  | 
|  | auto Buffer = std::move(*BufferOrErr); | 
|  | std::unique_ptr<InterfaceFile> IF; | 
|  | switch (identify_magic(Buffer->getBuffer())) { | 
|  | case file_magic::macho_dynamically_linked_shared_lib: | 
|  | case file_magic::macho_dynamically_linked_shared_lib_stub: | 
|  | case file_magic::macho_universal_binary: | 
|  | return DylibReader::get(Buffer->getMemBufferRef()); | 
|  | break; | 
|  | case file_magic::tapi_file: | 
|  | return TextAPIReader::get(Buffer->getMemBufferRef()); | 
|  | default: | 
|  | return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat, | 
|  | "unsupported library file format"); | 
|  | } | 
|  | llvm_unreachable("unexpected failure in getInterface"); | 
|  | } | 
|  |  | 
|  | std::pair<LibAttrs, ReexportedInterfaces> Options::getReexportedLibraries() { | 
|  | LibAttrs Reexports; | 
|  | ReexportedInterfaces ReexportIFs; | 
|  | auto AccumulateReexports = [&](StringRef Path, const ArchitectureSet &Archs) { | 
|  | auto ReexportIFOrErr = getInterfaceFile(Path); | 
|  | if (!ReexportIFOrErr) | 
|  | return false; | 
|  | std::unique_ptr<InterfaceFile> Reexport = std::move(*ReexportIFOrErr); | 
|  | StringRef InstallName = Reexport->getInstallName(); | 
|  | assert(!InstallName.empty() && "Parse error for install name"); | 
|  | Reexports.insert({InstallName, Archs}); | 
|  | ReexportIFs.emplace_back(std::move(*Reexport)); | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | PlatformSet Platforms; | 
|  | llvm::for_each(DriverOpts.Targets, | 
|  | [&](const auto &T) { Platforms.insert(T.first.Platform); }); | 
|  | // Populate search paths by looking at user paths before system ones. | 
|  | PathSeq FwkSearchPaths(FEOpts.FwkPaths.begin(), FEOpts.FwkPaths.end()); | 
|  | for (const PlatformType P : Platforms) { | 
|  | PathSeq PlatformSearchPaths = getPathsForPlatform(FEOpts.SystemFwkPaths, P); | 
|  | FwkSearchPaths.insert(FwkSearchPaths.end(), PlatformSearchPaths.begin(), | 
|  | PlatformSearchPaths.end()); | 
|  | for (const StringMapEntry<ArchitectureSet> &Lib : | 
|  | LinkerOpts.ReexportedFrameworks) { | 
|  | std::string Name = (Lib.getKey() + ".framework/" + Lib.getKey()).str(); | 
|  | std::string Path = findLibrary(Name, *FM, FwkSearchPaths, {}, {}); | 
|  | if (Path.empty()) { | 
|  | Diags->Report(diag::err_cannot_find_reexport) << false << Lib.getKey(); | 
|  | return {}; | 
|  | } | 
|  | if (DriverOpts.TraceLibraryLocation) | 
|  | errs() << Path << "\n"; | 
|  |  | 
|  | AccumulateReexports(Path, Lib.getValue()); | 
|  | } | 
|  | FwkSearchPaths.resize(FwkSearchPaths.size() - PlatformSearchPaths.size()); | 
|  | } | 
|  |  | 
|  | for (const StringMapEntry<ArchitectureSet> &Lib : | 
|  | LinkerOpts.ReexportedLibraries) { | 
|  | std::string Name = "lib" + Lib.getKey().str() + ".dylib"; | 
|  | std::string Path = findLibrary(Name, *FM, {}, LinkerOpts.LibPaths, {}); | 
|  | if (Path.empty()) { | 
|  | Diags->Report(diag::err_cannot_find_reexport) << true << Lib.getKey(); | 
|  | return {}; | 
|  | } | 
|  | if (DriverOpts.TraceLibraryLocation) | 
|  | errs() << Path << "\n"; | 
|  |  | 
|  | AccumulateReexports(Path, Lib.getValue()); | 
|  | } | 
|  |  | 
|  | for (const StringMapEntry<ArchitectureSet> &Lib : | 
|  | LinkerOpts.ReexportedLibraryPaths) | 
|  | AccumulateReexports(Lib.getKey(), Lib.getValue()); | 
|  |  | 
|  | return {std::move(Reexports), std::move(ReexportIFs)}; | 
|  | } | 
|  |  | 
|  | InstallAPIContext Options::createContext() { | 
|  | InstallAPIContext Ctx; | 
|  | Ctx.FM = FM; | 
|  | Ctx.Diags = Diags; | 
|  |  | 
|  | // InstallAPI requires two level namespacing. | 
|  | Ctx.BA.TwoLevelNamespace = true; | 
|  |  | 
|  | Ctx.BA.InstallName = LinkerOpts.InstallName; | 
|  | Ctx.BA.CurrentVersion = LinkerOpts.CurrentVersion; | 
|  | Ctx.BA.CompatVersion = LinkerOpts.CompatVersion; | 
|  | Ctx.BA.AppExtensionSafe = LinkerOpts.AppExtensionSafe; | 
|  | Ctx.BA.ParentUmbrella = LinkerOpts.ParentUmbrella; | 
|  | Ctx.BA.OSLibNotForSharedCache = LinkerOpts.OSLibNotForSharedCache; | 
|  | Ctx.FT = DriverOpts.OutFT; | 
|  | Ctx.OutputLoc = DriverOpts.OutputPath; | 
|  | Ctx.LangMode = FEOpts.LangMode; | 
|  |  | 
|  | auto [Reexports, ReexportedIFs] = getReexportedLibraries(); | 
|  | if (Diags->hasErrorOccurred()) | 
|  | return Ctx; | 
|  | Ctx.Reexports = Reexports; | 
|  |  | 
|  | // Collect symbols from alias lists. | 
|  | AliasMap Aliases; | 
|  | for (const StringRef ListPath : LinkerOpts.AliasLists) { | 
|  | auto Buffer = FM->getBufferForFile(ListPath); | 
|  | if (auto Err = Buffer.getError()) { | 
|  | Diags->Report(diag::err_cannot_open_file) << ListPath << Err.message(); | 
|  | return Ctx; | 
|  | } | 
|  | Expected<AliasMap> Result = parseAliasList(Buffer.get()); | 
|  | if (!Result) { | 
|  | Diags->Report(diag::err_cannot_read_input_list) | 
|  | << "symbol alias" << ListPath << toString(Result.takeError()); | 
|  | return Ctx; | 
|  | } | 
|  | Aliases.insert(Result.get().begin(), Result.get().end()); | 
|  | } | 
|  |  | 
|  | // Attempt to find umbrella headers by capturing framework name. | 
|  | StringRef FrameworkName; | 
|  | if (!LinkerOpts.IsDylib) | 
|  | FrameworkName = | 
|  | Library::getFrameworkNameFromInstallName(LinkerOpts.InstallName); | 
|  |  | 
|  | /// Process inputs headers. | 
|  | // 1. For headers discovered by directory scanning, sort them. | 
|  | // 2. For headers discovered by filelist, respect ordering. | 
|  | // 3. Append extra headers and mark any excluded headers. | 
|  | // 4. Finally, surface up umbrella headers to top of the list. | 
|  | if (!DriverOpts.InputDirectory.empty()) { | 
|  | DirectoryScanner Scanner(*FM, LinkerOpts.IsDylib | 
|  | ? ScanMode::ScanDylibs | 
|  | : ScanMode::ScanFrameworks); | 
|  | SmallString<PATH_MAX> NormalizedPath(DriverOpts.InputDirectory); | 
|  | FM->getVirtualFileSystem().makeAbsolute(NormalizedPath); | 
|  | sys::path::remove_dots(NormalizedPath, /*remove_dot_dot=*/true); | 
|  | if (llvm::Error Err = Scanner.scan(NormalizedPath)) { | 
|  | Diags->Report(diag::err_directory_scanning) | 
|  | << DriverOpts.InputDirectory << std::move(Err); | 
|  | return Ctx; | 
|  | } | 
|  | std::vector<Library> InputLibraries = Scanner.takeLibraries(); | 
|  | if (InputLibraries.size() > 1) { | 
|  | Diags->Report(diag::err_more_than_one_library); | 
|  | return Ctx; | 
|  | } | 
|  | llvm::append_range(Ctx.InputHeaders, | 
|  | DirectoryScanner::getHeaders(InputLibraries)); | 
|  | llvm::stable_sort(Ctx.InputHeaders); | 
|  | } | 
|  |  | 
|  | for (const StringRef ListPath : DriverOpts.FileLists) { | 
|  | auto Buffer = FM->getBufferForFile(ListPath); | 
|  | if (auto Err = Buffer.getError()) { | 
|  | Diags->Report(diag::err_cannot_open_file) << ListPath << Err.message(); | 
|  | return Ctx; | 
|  | } | 
|  | if (auto Err = FileListReader::loadHeaders(std::move(Buffer.get()), | 
|  | Ctx.InputHeaders, FM)) { | 
|  | Diags->Report(diag::err_cannot_read_input_list) | 
|  | << "header file" << ListPath << std::move(Err); | 
|  | return Ctx; | 
|  | } | 
|  | } | 
|  | // After initial input has been processed, add any extra headers. | 
|  | auto HandleExtraHeaders = [&](PathSeq &Headers, HeaderType Type) -> bool { | 
|  | assert(Type != HeaderType::Unknown && "Missing header type."); | 
|  | for (const StringRef Path : Headers) { | 
|  | if (!FM->getOptionalFileRef(Path)) { | 
|  | Diags->Report(diag::err_no_such_header_file) << Path << (unsigned)Type; | 
|  | return false; | 
|  | } | 
|  | SmallString<PATH_MAX> FullPath(Path); | 
|  | FM->makeAbsolutePath(FullPath); | 
|  |  | 
|  | auto IncludeName = createIncludeHeaderName(FullPath); | 
|  | Ctx.InputHeaders.emplace_back( | 
|  | FullPath, Type, IncludeName.has_value() ? *IncludeName : ""); | 
|  | Ctx.InputHeaders.back().setExtra(); | 
|  | } | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | if (!HandleExtraHeaders(DriverOpts.ExtraPublicHeaders, HeaderType::Public) || | 
|  | !HandleExtraHeaders(DriverOpts.ExtraPrivateHeaders, | 
|  | HeaderType::Private) || | 
|  | !HandleExtraHeaders(DriverOpts.ExtraProjectHeaders, HeaderType::Project)) | 
|  | return Ctx; | 
|  |  | 
|  | // After all headers have been added, consider excluded headers. | 
|  | std::vector<std::unique_ptr<HeaderGlob>> ExcludedHeaderGlobs; | 
|  | std::set<FileEntryRef> ExcludedHeaderFiles; | 
|  | auto ParseGlobs = [&](const PathSeq &Paths, HeaderType Type) { | 
|  | assert(Type != HeaderType::Unknown && "Missing header type."); | 
|  | for (const StringRef Path : Paths) { | 
|  | auto Glob = HeaderGlob::create(Path, Type); | 
|  | if (Glob) | 
|  | ExcludedHeaderGlobs.emplace_back(std::move(Glob.get())); | 
|  | else { | 
|  | consumeError(Glob.takeError()); | 
|  | if (auto File = FM->getFileRef(Path)) | 
|  | ExcludedHeaderFiles.emplace(*File); | 
|  | else { | 
|  | Diags->Report(diag::err_no_such_header_file) | 
|  | << Path << (unsigned)Type; | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | if (!ParseGlobs(DriverOpts.ExcludePublicHeaders, HeaderType::Public) || | 
|  | !ParseGlobs(DriverOpts.ExcludePrivateHeaders, HeaderType::Private) || | 
|  | !ParseGlobs(DriverOpts.ExcludeProjectHeaders, HeaderType::Project)) | 
|  | return Ctx; | 
|  |  | 
|  | for (HeaderFile &Header : Ctx.InputHeaders) { | 
|  | for (auto &Glob : ExcludedHeaderGlobs) | 
|  | if (Glob->match(Header)) | 
|  | Header.setExcluded(); | 
|  | } | 
|  | if (!ExcludedHeaderFiles.empty()) { | 
|  | for (HeaderFile &Header : Ctx.InputHeaders) { | 
|  | auto FileRef = FM->getFileRef(Header.getPath()); | 
|  | if (!FileRef) | 
|  | continue; | 
|  | if (ExcludedHeaderFiles.count(*FileRef)) | 
|  | Header.setExcluded(); | 
|  | } | 
|  | } | 
|  | // Report if glob was ignored. | 
|  | for (const auto &Glob : ExcludedHeaderGlobs) | 
|  | if (!Glob->didMatch()) | 
|  | Diags->Report(diag::warn_glob_did_not_match) << Glob->str(); | 
|  |  | 
|  | // Mark any explicit or inferred umbrella headers. If one exists, move | 
|  | // that to the beginning of the input headers. | 
|  | auto MarkandMoveUmbrellaInHeaders = [&](llvm::Regex &Regex, | 
|  | HeaderType Type) -> bool { | 
|  | auto It = find_if(Ctx.InputHeaders, [&Regex, Type](const HeaderFile &H) { | 
|  | return (H.getType() == Type) && Regex.match(H.getPath()); | 
|  | }); | 
|  |  | 
|  | if (It == Ctx.InputHeaders.end()) | 
|  | return false; | 
|  | It->setUmbrellaHeader(); | 
|  |  | 
|  | // Because there can be an umbrella header per header type, | 
|  | // find the first non umbrella header to swap position with. | 
|  | auto BeginPos = find_if(Ctx.InputHeaders, [](const HeaderFile &H) { | 
|  | return !H.isUmbrellaHeader(); | 
|  | }); | 
|  | if (BeginPos != Ctx.InputHeaders.end() && BeginPos < It) | 
|  | std::swap(*BeginPos, *It); | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | auto FindUmbrellaHeader = [&](StringRef HeaderPath, HeaderType Type) -> bool { | 
|  | assert(Type != HeaderType::Unknown && "Missing header type."); | 
|  | if (!HeaderPath.empty()) { | 
|  | auto EscapedString = Regex::escape(HeaderPath); | 
|  | Regex UmbrellaRegex(EscapedString); | 
|  | if (!MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type)) { | 
|  | Diags->Report(diag::err_no_such_umbrella_header_file) | 
|  | << HeaderPath << (unsigned)Type; | 
|  | return false; | 
|  | } | 
|  | } else if (!FrameworkName.empty() && (Type != HeaderType::Project)) { | 
|  | auto UmbrellaName = "/" + Regex::escape(FrameworkName); | 
|  | if (Type == HeaderType::Public) | 
|  | UmbrellaName += "\\.h"; | 
|  | else | 
|  | UmbrellaName += "[_]?Private\\.h"; | 
|  | Regex UmbrellaRegex(UmbrellaName); | 
|  | MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type); | 
|  | } | 
|  | return true; | 
|  | }; | 
|  | if (!FindUmbrellaHeader(DriverOpts.PublicUmbrellaHeader, | 
|  | HeaderType::Public) || | 
|  | !FindUmbrellaHeader(DriverOpts.PrivateUmbrellaHeader, | 
|  | HeaderType::Private) || | 
|  | !FindUmbrellaHeader(DriverOpts.ProjectUmbrellaHeader, | 
|  | HeaderType::Project)) | 
|  | return Ctx; | 
|  |  | 
|  | // Parse binary dylib and initialize verifier. | 
|  | if (DriverOpts.DylibToVerify.empty()) { | 
|  | Ctx.Verifier = std::make_unique<DylibVerifier>(); | 
|  | return Ctx; | 
|  | } | 
|  |  | 
|  | auto Buffer = FM->getBufferForFile(DriverOpts.DylibToVerify); | 
|  | if (auto Err = Buffer.getError()) { | 
|  | Diags->Report(diag::err_cannot_open_file) | 
|  | << DriverOpts.DylibToVerify << Err.message(); | 
|  | return Ctx; | 
|  | } | 
|  |  | 
|  | DylibReader::ParseOption PO; | 
|  | PO.Undefineds = false; | 
|  | Expected<Records> Slices = | 
|  | DylibReader::readFile((*Buffer)->getMemBufferRef(), PO); | 
|  | if (auto Err = Slices.takeError()) { | 
|  | Diags->Report(diag::err_cannot_open_file) | 
|  | << DriverOpts.DylibToVerify << std::move(Err); | 
|  | return Ctx; | 
|  | } | 
|  |  | 
|  | Ctx.Verifier = std::make_unique<DylibVerifier>( | 
|  | std::move(*Slices), std::move(ReexportedIFs), std::move(Aliases), Diags, | 
|  | DriverOpts.VerifyMode, DriverOpts.Zippered, DriverOpts.Demangle, | 
|  | DriverOpts.DSYMPath); | 
|  | return Ctx; | 
|  | } | 
|  |  | 
|  | void Options::addConditionalCC1Args(std::vector<std::string> &ArgStrings, | 
|  | const llvm::Triple &Targ, | 
|  | const HeaderType Type) { | 
|  | // Unique to architecture (Xarch) options hold no arguments to pass along for | 
|  | // frontend. | 
|  |  | 
|  | // Add specific to platform arguments. | 
|  | PathSeq PlatformSearchPaths = | 
|  | getPathsForPlatform(FEOpts.SystemFwkPaths, mapToPlatformType(Targ)); | 
|  | llvm::for_each(PlatformSearchPaths, [&ArgStrings](const StringRef Path) { | 
|  | ArgStrings.push_back("-iframework"); | 
|  | ArgStrings.push_back(Path.str()); | 
|  | }); | 
|  |  | 
|  | // Add specific to header type arguments. | 
|  | if (Type == HeaderType::Project) | 
|  | for (const StringRef A : ProjectLevelArgs) | 
|  | ArgStrings.emplace_back(A); | 
|  | } | 
|  |  | 
|  | } // namespace installapi | 
|  | } // namespace clang |