//===-- 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/FileList.h" #include "clang/InstallAPI/HeaderFile.h" #include "clang/InstallAPI/InstallAPIDiagnostic.h" #include "llvm/BinaryFormat/Magic.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 { /// Create prefix string literals used in InstallAPIOpts.td. #define PREFIX(NAME, VALUE) \ static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \ static constexpr llvm::ArrayRef NAME( \ NAME##_init, std::size(NAME##_init) - 1); #include "InstallAPIOpts.inc" #undef PREFIX static constexpr const llvm::StringLiteral PrefixTable_init[] = #define PREFIX_UNION(VALUES) VALUES #include "InstallAPIOpts.inc" #undef PREFIX_UNION ; static constexpr const ArrayRef PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1); /// Create table mapping all options defined in InstallAPIOpts.td. static constexpr OptTable::Info InfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \ VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \ VALUES) \ {PREFIX, \ NAME, \ HELPTEXT, \ HELPTEXTSFORVARIANTS, \ METAVAR, \ OPT_##ID, \ Option::KIND##Class, \ PARAM, \ FLAGS, \ VISIBILITY, \ OPT_##GROUP, \ OPT_##ALIAS, \ ALIASARGS, \ VALUES}, #include "InstallAPIOpts.inc" #undef OPTION }; namespace { /// \brief Create OptTable class for parsing actual command line arguments. class DriverOptTable : public opt::PrecomputedOptTable { public: DriverOptTable() : PrecomputedOptTable(InfoTable, PrefixTable) {} }; } // end anonymous namespace. static llvm::opt::OptTable *createDriverOptTable() { return new DriverOptTable(); } bool Options::processDriverOptions(InputArgList &Args) { // Handle inputs. llvm::append_range(DriverOpts.FileLists, Args.getAllArgValues(drv::OPT_INPUT)); // 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_triple); 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; } } 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) { if ((*It)->getOption().matches(OPT_Xarch__)) { if (!processXarchOption(Args, It)) return false; } } // TODO: Add support for the all of the X* options installapi supports. 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::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); 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. // TODO: Support passing framework paths per platform. for (const Arg *A : Args.filtered(drv::OPT_iframework)) FEOpts.SystemFwkPaths.emplace_back(A->getValue()); // 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()); } return true; } bool Options::addFilePaths(InputArgList &Args, PathSeq &Headers, OptSpecifier ID) { for (const StringRef Path : Args.getAllArgValues(ID)) { if ((bool)FM->getDirectory(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 (std::string &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 {}; 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)) { 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 ClangDriverArgs(ParsedArgs.size()); for (const Arg *A : ParsedArgs) { if (A->isClaimed()) continue; llvm::copy(A->getValues(), std::back_inserter(ClangDriverArgs)); } 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; llvm::for_each(DriverOpts.Targets, [&AllArchs](const auto &T) { AllArchs.set(T.first.Arch); }); auto assignDefaultLibAttrs = [&AllArchs](LibAttrs &Attrs) { for (StringMapEntry &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 const Regex Rule("(.+)/(.+)\\.framework/"); static StringRef getFrameworkNameFromInstallName(StringRef InstallName) { SmallVector Match; Rule.match(InstallName, &Match); if (Match.empty()) return ""; return Match.back(); } 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); std::unique_ptr IF; switch (identify_magic(Buffer->getBuffer())) { case file_magic::macho_dynamically_linked_shared_lib: LLVM_FALLTHROUGH; case file_magic::macho_dynamically_linked_shared_lib_stub: LLVM_FALLTHROUGH; 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.insert({InstallName, Archs}); ReexportIFs.emplace_back(std::move(*Reexport)); return true; }; // Populate search paths by looking at user paths before system ones. PathSeq FwkSearchPaths(FEOpts.FwkPaths.begin(), FEOpts.FwkPaths.end()); // FIXME: System framework paths need to reset if installapi is invoked with // different platforms. FwkSearchPaths.insert(FwkSearchPaths.end(), FEOpts.SystemFwkPaths.begin(), FEOpts.SystemFwkPaths.end()); for (const StringMapEntry &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 &Lib : LinkerOpts.ReexportedLibraryPaths) AccumulateReexports(Lib.getKey(), Lib.getValue()); for (const StringMapEntry &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()); } 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; // Attempt to find umbrella headers by capturing framework name. StringRef FrameworkName; if (!LinkerOpts.IsDylib) FrameworkName = getFrameworkNameFromInstallName(LinkerOpts.InstallName); // Process inputs. for (const std::string &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)) { Diags->Report(diag::err_cannot_open_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), Diags, DriverOpts.VerifyMode, DriverOpts.Demangle, DriverOpts.DSYMPath); return Ctx; } } // namespace installapi } // namespace clang