//===-- 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 getArgListFromJSON(const StringRef Input, llvm::opt::OptTable *Table, std::vector &Storage) { using namespace json; Expected 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(TextAPIErrorCode::InvalidInputFormat); for (auto Arg : *ArgList) { std::optional ArgStr = Arg.getAsString(); if (!ArgStr) return make_error(TextAPIErrorCode::InvalidInputFormat); Storage.emplace_back(Label); Storage.emplace_back(*ArgStr); } } std::vector CArgs(Storage.size()); for (StringRef Str : Storage) 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 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(); auto &UniqueArgs = FEOpts.UniqueArgs[Label]; if (AValues.empty()) UniqueArgs.emplace_back(ASpelling.str()); else for (const StringRef Val : AValues) UniqueArgs.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 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(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(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{}); // 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(FEOpts.ISysroot); sys::path::append(Path, LibPath); LinkerOpts.LibPaths.emplace_back(Path.str()); } for (const StringRef FwkPath : DefaultFrameworkPaths) { SmallString Path(FEOpts.ISysroot); sys::path::append(Path, FwkPath); FEOpts.SystemFwkPaths.emplace_back(Path.str(), std::optional{}); } 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 Options::processAndFilterOutInstallAPIOptions(ArrayRef Args) { std::unique_ptr 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(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)) { auto It = ArgToArchMap.find(A); LinkerOpts.AllowableClients.getArchSet(A->getValue()) = It != ArgToArchMap.end() ? It->second : ArchitectureSet(); A->claim(); } for (const Arg *A : ParsedArgs.filtered(OPT_reexport_l)) { auto It = ArgToArchMap.find(A); LinkerOpts.ReexportedLibraries.getArchSet(A->getValue()) = It != ArgToArchMap.end() ? It->second : ArchitectureSet(); A->claim(); } for (const Arg *A : ParsedArgs.filtered(OPT_reexport_library)) { auto It = ArgToArchMap.find(A); LinkerOpts.ReexportedLibraryPaths.getArchSet(A->getValue()) = It != ArgToArchMap.end() ? It->second : ArchitectureSet(); A->claim(); } for (const Arg *A : ParsedArgs.filtered(OPT_reexport_framework)) { auto It = ArgToArchMap.find(A); LinkerOpts.ReexportedFrameworks.getArchSet(A->getValue()) = It != ArgToArchMap.end() ? It->second : ArchitectureSet(); A->claim(); } for (const Arg *A : ParsedArgs.filtered(OPT_rpath)) { auto It = ArgToArchMap.find(A); LinkerOpts.RPaths.getArchSet(A->getValue()) = It != ArgToArchMap.end() ? It->second : 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 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::append_range(ClangDriverArgs, A->getValues()); } return ClangDriverArgs; } Options::Options(DiagnosticsEngine &Diag, FileManager *FM, ArrayRef 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; for (const auto &T : DriverOpts.Targets) AllArchs.set(T.first.Arch); auto assignDefaultLibAttrs = [&AllArchs](LibAttrs &Attrs) { for (auto &[_, Archs] : Attrs.get()) if (Archs.empty()) Archs = 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::append_range(FrontendArgs, A->getValues()); } } static Expected> getInterfaceFile(const StringRef Filename) { ErrorOr> BufferOrErr = MemoryBuffer::getFile(Filename); if (auto Err = BufferOrErr.getError()) return errorCodeToError(std::move(Err)); auto Buffer = std::move(*BufferOrErr); 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(TextAPIErrorCode::InvalidInputFormat, "unsupported library file format"); } llvm_unreachable("unexpected failure in getInterface"); } std::pair Options::getReexportedLibraries() { LibAttrs Reexports; ReexportedInterfaces ReexportIFs; auto AccumulateReexports = [&](StringRef Path, const ArchitectureSet &Archs) { auto ReexportIFOrErr = getInterfaceFile(Path); if (!ReexportIFOrErr) return false; std::unique_ptr Reexport = std::move(*ReexportIFOrErr); StringRef InstallName = Reexport->getInstallName(); assert(!InstallName.empty() && "Parse error for install name"); Reexports.getArchSet(InstallName) = Archs; ReexportIFs.emplace_back(std::move(*Reexport)); return true; }; PlatformSet Platforms; for (const auto &T : DriverOpts.Targets) 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); llvm::append_range(FwkSearchPaths, PlatformSearchPaths); for (const auto &[Lib, Archs] : LinkerOpts.ReexportedFrameworks.get()) { std::string Name = (Lib + ".framework/" + Lib); std::string Path = findLibrary(Name, *FM, FwkSearchPaths, {}, {}); if (Path.empty()) { Diags->Report(diag::err_cannot_find_reexport) << false << Lib; return {}; } if (DriverOpts.TraceLibraryLocation) errs() << Path << "\n"; AccumulateReexports(Path, Archs); } FwkSearchPaths.resize(FwkSearchPaths.size() - PlatformSearchPaths.size()); } for (const auto &[Lib, Archs] : LinkerOpts.ReexportedLibraries.get()) { std::string Name = "lib" + Lib + ".dylib"; std::string Path = findLibrary(Name, *FM, {}, LinkerOpts.LibPaths, {}); if (Path.empty()) { Diags->Report(diag::err_cannot_find_reexport) << true << Lib; return {}; } if (DriverOpts.TraceLibraryLocation) errs() << Path << "\n"; AccumulateReexports(Path, Archs); } for (const auto &[Lib, Archs] : LinkerOpts.ReexportedLibraryPaths.get()) AccumulateReexports(Lib, Archs); 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 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 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 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 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> ExcludedHeaderGlobs; std::set 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(); 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 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( 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 &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)); for (StringRef Path : PlatformSearchPaths) { 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