blob: 53340da704fc0715a91c73a2afb4ce04c2772367 [file] [log] [blame]
//===-- 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<llvm::StringLiteral> 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<StringLiteral>
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<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::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->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 (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 {};
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 const Regex Rule("(.+)/(.+)\\.framework/");
static StringRef getFrameworkNameFromInstallName(StringRef InstallName) {
SmallVector<StringRef, 3> Match;
Rule.match(InstallName, &Match);
if (Match.empty())
return "";
return Match.back();
}
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)
<< /*IsFileList=*/false << 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 = getFrameworkNameFromInstallName(LinkerOpts.InstallName);
// Process inputs.
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)
<< /*IsFileList=*/true << 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