1//===-- Options.cpp -------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Options.h"
10#include "clang/Basic/DiagnosticIDs.h"
11#include "clang/Driver/Driver.h"
12#include "clang/InstallAPI/FileList.h"
13#include "clang/InstallAPI/HeaderFile.h"
14#include "clang/InstallAPI/InstallAPIDiagnostic.h"
15#include "llvm/BinaryFormat/Magic.h"
16#include "llvm/Support/Program.h"
17#include "llvm/TargetParser/Host.h"
18#include "llvm/TextAPI/DylibReader.h"
19#include "llvm/TextAPI/TextAPIError.h"
20#include "llvm/TextAPI/TextAPIReader.h"
21#include "llvm/TextAPI/TextAPIWriter.h"
22
23using namespace llvm;
24using namespace llvm::opt;
25using namespace llvm::MachO;
26
27namespace drv = clang::driver::options;
28
29namespace clang {
30namespace installapi {
31
32/// Create prefix string literals used in InstallAPIOpts.td.
33#define PREFIX(NAME, VALUE) \
34 static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
35 static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
36 NAME##_init, std::size(NAME##_init) - 1);
37#include "InstallAPIOpts.inc"
38#undef PREFIX
39
40static constexpr const llvm::StringLiteral PrefixTable_init[] =
41#define PREFIX_UNION(VALUES) VALUES
42#include "InstallAPIOpts.inc"
43#undef PREFIX_UNION
44 ;
45static constexpr const ArrayRef<StringLiteral>
46 PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1);
47
48/// Create table mapping all options defined in InstallAPIOpts.td.
49static constexpr OptTable::Info InfoTable[] = {
50#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
51 VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
52 VALUES) \
53 {PREFIX, \
54 NAME, \
55 HELPTEXT, \
56 HELPTEXTSFORVARIANTS, \
57 METAVAR, \
58 OPT_##ID, \
59 Option::KIND##Class, \
60 PARAM, \
61 FLAGS, \
62 VISIBILITY, \
63 OPT_##GROUP, \
64 OPT_##ALIAS, \
65 ALIASARGS, \
66 VALUES},
67#include "InstallAPIOpts.inc"
68#undef OPTION
69};
70
71namespace {
72
73/// \brief Create OptTable class for parsing actual command line arguments.
74class DriverOptTable : public opt::PrecomputedOptTable {
75public:
76 DriverOptTable() : PrecomputedOptTable(InfoTable, PrefixTable) {}
77};
78
79} // end anonymous namespace.
80
81static llvm::opt::OptTable *createDriverOptTable() {
82 return new DriverOptTable();
83}
84
85bool Options::processDriverOptions(InputArgList &Args) {
86 // Handle inputs.
87 llvm::append_range(DriverOpts.FileLists,
88 Args.getAllArgValues(drv::OPT_INPUT));
89
90 // Handle output.
91 SmallString<PATH_MAX> OutputPath;
92 if (auto *Arg = Args.getLastArg(drv::OPT_o)) {
93 OutputPath = Arg->getValue();
94 if (OutputPath != "-")
95 FM->makeAbsolutePath(Path&: OutputPath);
96 DriverOpts.OutputPath = std::string(OutputPath);
97 }
98 if (DriverOpts.OutputPath.empty()) {
99 Diags->Report(diag::err_no_output_file);
100 return false;
101 }
102
103 // Do basic error checking first for mixing -target and -arch options.
104 auto *ArgArch = Args.getLastArgNoClaim(drv::OPT_arch);
105 auto *ArgTarget = Args.getLastArgNoClaim(drv::OPT_target);
106 auto *ArgTargetVariant =
107 Args.getLastArgNoClaim(drv::OPT_darwin_target_variant);
108 if (ArgArch && (ArgTarget || ArgTargetVariant)) {
109 Diags->Report(clang::diag::err_drv_argument_not_allowed_with)
110 << ArgArch->getAsString(Args)
111 << (ArgTarget ? ArgTarget : ArgTargetVariant)->getAsString(Args);
112 return false;
113 }
114
115 auto *ArgMinTargetOS = Args.getLastArgNoClaim(drv::OPT_mtargetos_EQ);
116 if ((ArgTarget || ArgTargetVariant) && ArgMinTargetOS) {
117 Diags->Report(clang::diag::err_drv_cannot_mix_options)
118 << ArgTarget->getAsString(Args) << ArgMinTargetOS->getAsString(Args);
119 return false;
120 }
121
122 // Capture target triples first.
123 if (ArgTarget) {
124 for (const Arg *A : Args.filtered(drv::OPT_target)) {
125 A->claim();
126 llvm::Triple TargetTriple(A->getValue());
127 Target TAPITarget = Target(TargetTriple);
128 if ((TAPITarget.Arch == AK_unknown) ||
129 (TAPITarget.Platform == PLATFORM_UNKNOWN)) {
130 Diags->Report(clang::diag::err_drv_unsupported_opt_for_target)
131 << "installapi" << TargetTriple.str();
132 return false;
133 }
134 DriverOpts.Targets[TAPITarget] = TargetTriple;
135 }
136 }
137
138 // Capture target variants.
139 DriverOpts.Zippered = ArgTargetVariant != nullptr;
140 for (Arg *A : Args.filtered(drv::OPT_darwin_target_variant)) {
141 A->claim();
142 Triple Variant(A->getValue());
143 if (Variant.getVendor() != Triple::Apple) {
144 Diags->Report(diag::err_unsupported_vendor)
145 << Variant.getVendorName() << A->getAsString(Args);
146 return false;
147 }
148
149 switch (Variant.getOS()) {
150 default:
151 Diags->Report(diag::err_unsupported_os)
152 << Variant.getOSName() << A->getAsString(Args);
153 return false;
154 case Triple::MacOSX:
155 case Triple::IOS:
156 break;
157 }
158
159 switch (Variant.getEnvironment()) {
160 default:
161 Diags->Report(diag::err_unsupported_environment)
162 << Variant.getEnvironmentName() << A->getAsString(Args);
163 return false;
164 case Triple::UnknownEnvironment:
165 case Triple::MacABI:
166 break;
167 }
168
169 Target TAPIVariant(Variant);
170 // See if there is a matching --target option for this --target-variant
171 // option.
172 auto It = find_if(DriverOpts.Targets, [&](const auto &T) {
173 return (T.first.Arch == TAPIVariant.Arch) &&
174 (T.first.Platform != PlatformType::PLATFORM_UNKNOWN);
175 });
176
177 if (It == DriverOpts.Targets.end()) {
178 Diags->Report(diag::err_no_matching_target) << Variant.str();
179 return false;
180 }
181
182 DriverOpts.Targets[TAPIVariant] = Variant;
183 }
184
185 DriverOpts.Verbose = Args.hasArgNoClaim(drv::OPT_v);
186
187 return true;
188}
189
190bool Options::processInstallAPIXOptions(InputArgList &Args) {
191 for (arg_iterator It = Args.begin(), End = Args.end(); It != End; ++It) {
192 if ((*It)->getOption().matches(OPT_Xarch__)) {
193 if (!processXarchOption(Args, Curr: It))
194 return false;
195 }
196 }
197 // TODO: Add support for the all of the X* options installapi supports.
198
199 return true;
200}
201
202bool Options::processXarchOption(InputArgList &Args, arg_iterator Curr) {
203 Arg *CurrArg = *Curr;
204 Architecture Arch = getArchitectureFromName(Name: CurrArg->getValue(N: 0));
205 if (Arch == AK_unknown) {
206 Diags->Report(diag::err_drv_invalid_arch_name)
207 << CurrArg->getAsString(Args);
208 return false;
209 }
210
211 auto NextIt = std::next(Curr);
212 if (NextIt == Args.end()) {
213 Diags->Report(diag::err_drv_missing_argument)
214 << CurrArg->getAsString(Args) << 1;
215 return false;
216 }
217
218 // InstallAPI has a limited understanding of supported Xarch options.
219 // Currently this is restricted to linker inputs.
220 const Arg *NextArg = *NextIt;
221 switch (NextArg->getOption().getID()) {
222 case OPT_allowable_client:
223 case OPT_reexport_l:
224 case OPT_reexport_framework:
225 case OPT_reexport_library:
226 case OPT_rpath:
227 break;
228 default:
229 Diags->Report(diag::err_drv_invalid_argument_to_option)
230 << NextArg->getAsString(Args) << CurrArg->getAsString(Args);
231 return false;
232 }
233
234 ArgToArchMap[NextArg] = Arch;
235 CurrArg->claim();
236
237 return true;
238}
239
240bool Options::processLinkerOptions(InputArgList &Args) {
241 // Handle required arguments.
242 if (const Arg *A = Args.getLastArg(drv::OPT_install__name))
243 LinkerOpts.InstallName = A->getValue();
244 if (LinkerOpts.InstallName.empty()) {
245 Diags->Report(diag::err_no_install_name);
246 return false;
247 }
248
249 // Defaulted or optional arguments.
250 if (auto *Arg = Args.getLastArg(drv::OPT_current__version))
251 LinkerOpts.CurrentVersion.parse64(Str: Arg->getValue());
252
253 if (auto *Arg = Args.getLastArg(drv::OPT_compatibility__version))
254 LinkerOpts.CompatVersion.parse64(Str: Arg->getValue());
255
256 if (auto *Arg = Args.getLastArg(drv::OPT_compatibility__version))
257 LinkerOpts.CompatVersion.parse64(Str: Arg->getValue());
258
259 if (auto *Arg = Args.getLastArg(drv::OPT_umbrella))
260 LinkerOpts.ParentUmbrella = Arg->getValue();
261
262 LinkerOpts.IsDylib = Args.hasArg(drv::OPT_dynamiclib);
263
264 for (auto *Arg : Args.filtered(drv::OPT_alias_list)) {
265 LinkerOpts.AliasLists.emplace_back(Arg->getValue());
266 Arg->claim();
267 }
268
269 LinkerOpts.AppExtensionSafe = Args.hasFlag(
270 drv::OPT_fapplication_extension, drv::OPT_fno_application_extension,
271 /*Default=*/LinkerOpts.AppExtensionSafe);
272
273 if (::getenv(name: "LD_NO_ENCRYPT") != nullptr)
274 LinkerOpts.AppExtensionSafe = true;
275
276 if (::getenv(name: "LD_APPLICATION_EXTENSION_SAFE") != nullptr)
277 LinkerOpts.AppExtensionSafe = true;
278
279 // Capture library paths.
280 PathSeq LibraryPaths;
281 for (const Arg *A : Args.filtered(drv::OPT_L)) {
282 LibraryPaths.emplace_back(A->getValue());
283 A->claim();
284 }
285
286 if (!LibraryPaths.empty())
287 LinkerOpts.LibPaths = std::move(LibraryPaths);
288
289 return true;
290}
291
292// NOTE: Do not claim any arguments, as they will be passed along for CC1
293// invocations.
294bool Options::processFrontendOptions(InputArgList &Args) {
295 // Capture language mode.
296 if (auto *A = Args.getLastArgNoClaim(drv::OPT_x)) {
297 FEOpts.LangMode = llvm::StringSwitch<clang::Language>(A->getValue())
298 .Case("c", clang::Language::C)
299 .Case("c++", clang::Language::CXX)
300 .Case("objective-c", clang::Language::ObjC)
301 .Case("objective-c++", clang::Language::ObjCXX)
302 .Default(clang::Language::Unknown);
303
304 if (FEOpts.LangMode == clang::Language::Unknown) {
305 Diags->Report(clang::diag::err_drv_invalid_value)
306 << A->getAsString(Args) << A->getValue();
307 return false;
308 }
309 }
310 for (auto *A : Args.filtered(drv::OPT_ObjC, drv::OPT_ObjCXX)) {
311 if (A->getOption().matches(drv::OPT_ObjC))
312 FEOpts.LangMode = clang::Language::ObjC;
313 else
314 FEOpts.LangMode = clang::Language::ObjCXX;
315 }
316
317 // Capture Sysroot.
318 if (const Arg *A = Args.getLastArgNoClaim(drv::OPT_isysroot)) {
319 SmallString<PATH_MAX> Path(A->getValue());
320 FM->makeAbsolutePath(Path&: Path);
321 if (!FM->getOptionalDirectoryRef(DirName: Path)) {
322 Diags->Report(diag::err_missing_sysroot) << Path;
323 return false;
324 }
325 FEOpts.ISysroot = std::string(Path);
326 } else if (FEOpts.ISysroot.empty()) {
327 // Mirror CLANG and obtain the isysroot from the SDKROOT environment
328 // variable, if it wasn't defined by the command line.
329 if (auto *Env = ::getenv(name: "SDKROOT")) {
330 if (StringRef(Env) != "/" && llvm::sys::path::is_absolute(path: Env) &&
331 FM->getOptionalFileRef(Filename: Env))
332 FEOpts.ISysroot = Env;
333 }
334 }
335
336 // Capture system frameworks.
337 // TODO: Support passing framework paths per platform.
338 for (const Arg *A : Args.filtered(drv::OPT_iframework))
339 FEOpts.SystemFwkPaths.emplace_back(A->getValue());
340
341 // Capture framework paths.
342 PathSeq FrameworkPaths;
343 for (const Arg *A : Args.filtered(drv::OPT_F))
344 FrameworkPaths.emplace_back(A->getValue());
345
346 if (!FrameworkPaths.empty())
347 FEOpts.FwkPaths = std::move(FrameworkPaths);
348
349 // Add default framework/library paths.
350 PathSeq DefaultLibraryPaths = {"/usr/lib", "/usr/local/lib"};
351 PathSeq DefaultFrameworkPaths = {"/Library/Frameworks",
352 "/System/Library/Frameworks"};
353
354 for (const StringRef LibPath : DefaultLibraryPaths) {
355 SmallString<PATH_MAX> Path(FEOpts.ISysroot);
356 sys::path::append(Path, LibPath);
357 LinkerOpts.LibPaths.emplace_back(Path.str());
358 }
359 for (const StringRef FwkPath : DefaultFrameworkPaths) {
360 SmallString<PATH_MAX> Path(FEOpts.ISysroot);
361 sys::path::append(Path, FwkPath);
362 FEOpts.SystemFwkPaths.emplace_back(Path.str());
363 }
364
365 return true;
366}
367
368bool Options::addFilePaths(InputArgList &Args, PathSeq &Headers,
369 OptSpecifier ID) {
370 for (const StringRef Path : Args.getAllArgValues(ID)) {
371 if ((bool)FM->getDirectory(Path, /*CacheFailure=*/false)) {
372 auto InputHeadersOrErr = enumerateFiles(*FM, Path);
373 if (!InputHeadersOrErr) {
374 Diags->Report(diag::err_cannot_open_file)
375 << Path << toString(InputHeadersOrErr.takeError());
376 return false;
377 }
378 // Sort headers to ensure deterministic behavior.
379 sort(*InputHeadersOrErr);
380 for (StringRef H : *InputHeadersOrErr)
381 Headers.emplace_back(std::move(H));
382 } else
383 Headers.emplace_back(Path);
384 }
385 return true;
386}
387
388std::vector<const char *>
389Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) {
390 std::unique_ptr<llvm::opt::OptTable> Table;
391 Table.reset(createDriverOptTable());
392
393 unsigned MissingArgIndex, MissingArgCount;
394 auto ParsedArgs = Table->ParseArgs(Args.slice(1), MissingArgIndex,
395 MissingArgCount, Visibility());
396
397 // Capture InstallAPI only driver options.
398 if (!processInstallAPIXOptions(Args&: ParsedArgs))
399 return {};
400
401 DriverOpts.Demangle = ParsedArgs.hasArg(OPT_demangle);
402
403 if (auto *A = ParsedArgs.getLastArg(OPT_filetype)) {
404 DriverOpts.OutFT = TextAPIWriter::parseFileType(FT: A->getValue());
405 if (DriverOpts.OutFT == FileType::Invalid) {
406 Diags->Report(clang::diag::err_drv_invalid_value)
407 << A->getAsString(ParsedArgs) << A->getValue();
408 return {};
409 }
410 }
411
412 if (const Arg *A = ParsedArgs.getLastArg(OPT_verify_mode_EQ)) {
413 DriverOpts.VerifyMode =
414 StringSwitch<VerificationMode>(A->getValue())
415 .Case("ErrorsOnly", VerificationMode::ErrorsOnly)
416 .Case("ErrorsAndWarnings", VerificationMode::ErrorsAndWarnings)
417 .Case("Pedantic", VerificationMode::Pedantic)
418 .Default(VerificationMode::Invalid);
419
420 if (DriverOpts.VerifyMode == VerificationMode::Invalid) {
421 Diags->Report(clang::diag::err_drv_invalid_value)
422 << A->getAsString(ParsedArgs) << A->getValue();
423 return {};
424 }
425 }
426
427 if (const Arg *A = ParsedArgs.getLastArg(OPT_verify_against))
428 DriverOpts.DylibToVerify = A->getValue();
429
430 if (const Arg *A = ParsedArgs.getLastArg(OPT_dsym))
431 DriverOpts.DSYMPath = A->getValue();
432
433 DriverOpts.TraceLibraryLocation = ParsedArgs.hasArg(OPT_t);
434
435 // Linker options not handled by clang driver.
436 LinkerOpts.OSLibNotForSharedCache =
437 ParsedArgs.hasArg(OPT_not_for_dyld_shared_cache);
438
439 for (const Arg *A : ParsedArgs.filtered(OPT_allowable_client)) {
440 LinkerOpts.AllowableClients[A->getValue()] =
441 ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
442 A->claim();
443 }
444
445 for (const Arg *A : ParsedArgs.filtered(OPT_reexport_l)) {
446 LinkerOpts.ReexportedLibraries[A->getValue()] =
447 ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
448 A->claim();
449 }
450
451 for (const Arg *A : ParsedArgs.filtered(OPT_reexport_library)) {
452 LinkerOpts.ReexportedLibraryPaths[A->getValue()] =
453 ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
454 A->claim();
455 }
456
457 for (const Arg *A : ParsedArgs.filtered(OPT_reexport_framework)) {
458 LinkerOpts.ReexportedFrameworks[A->getValue()] =
459 ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
460 A->claim();
461 }
462
463 for (const Arg *A : ParsedArgs.filtered(OPT_rpath)) {
464 LinkerOpts.RPaths[A->getValue()] =
465 ArgToArchMap.count(A) ? ArgToArchMap[A] : ArchitectureSet();
466 A->claim();
467 }
468
469 // Handle exclude & extra header directories or files.
470 auto handleAdditionalInputArgs = [&](PathSeq &Headers,
471 clang::installapi::ID OptID) {
472 if (ParsedArgs.hasArgNoClaim(OptID))
473 Headers.clear();
474 return addFilePaths(ParsedArgs, Headers, OptID);
475 };
476
477 if (!handleAdditionalInputArgs(DriverOpts.ExtraPublicHeaders,
478 OPT_extra_public_header))
479 return {};
480
481 if (!handleAdditionalInputArgs(DriverOpts.ExtraPrivateHeaders,
482 OPT_extra_private_header))
483 return {};
484 if (!handleAdditionalInputArgs(DriverOpts.ExtraProjectHeaders,
485 OPT_extra_project_header))
486 return {};
487
488 if (!handleAdditionalInputArgs(DriverOpts.ExcludePublicHeaders,
489 OPT_exclude_public_header))
490 return {};
491 if (!handleAdditionalInputArgs(DriverOpts.ExcludePrivateHeaders,
492 OPT_exclude_private_header))
493 return {};
494 if (!handleAdditionalInputArgs(DriverOpts.ExcludeProjectHeaders,
495 OPT_exclude_project_header))
496 return {};
497
498 // Handle umbrella headers.
499 if (const Arg *A = ParsedArgs.getLastArg(OPT_public_umbrella_header))
500 DriverOpts.PublicUmbrellaHeader = A->getValue();
501
502 if (const Arg *A = ParsedArgs.getLastArg(OPT_private_umbrella_header))
503 DriverOpts.PrivateUmbrellaHeader = A->getValue();
504
505 if (const Arg *A = ParsedArgs.getLastArg(OPT_project_umbrella_header))
506 DriverOpts.ProjectUmbrellaHeader = A->getValue();
507
508 /// Any unclaimed arguments should be forwarded to the clang driver.
509 std::vector<const char *> ClangDriverArgs(ParsedArgs.size());
510 for (const Arg *A : ParsedArgs) {
511 if (A->isClaimed())
512 continue;
513 llvm::copy(A->getValues(), std::back_inserter(ClangDriverArgs));
514 }
515 return ClangDriverArgs;
516}
517
518Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
519 ArrayRef<const char *> Args, const StringRef ProgName)
520 : Diags(&Diag), FM(FM) {
521
522 // First process InstallAPI specific options.
523 auto DriverArgs = processAndFilterOutInstallAPIOptions(Args: Args);
524 if (Diags->hasErrorOccurred())
525 return;
526
527 // Set up driver to parse remaining input arguments.
528 clang::driver::Driver Driver(ProgName, llvm::sys::getDefaultTargetTriple(),
529 *Diags, "clang installapi tool");
530 auto TargetAndMode =
531 clang::driver::ToolChain::getTargetAndModeFromProgramName(ProgName);
532 Driver.setTargetAndMode(TargetAndMode);
533 bool HasError = false;
534 llvm::opt::InputArgList ArgList =
535 Driver.ParseArgStrings(Args: DriverArgs, /*UseDriverMode=*/true, ContainsError&: HasError);
536 if (HasError)
537 return;
538 Driver.setCheckInputsExist(false);
539
540 if (!processDriverOptions(Args&: ArgList))
541 return;
542
543 if (!processLinkerOptions(Args&: ArgList))
544 return;
545
546 if (!processFrontendOptions(Args&: ArgList))
547 return;
548
549 // After all InstallAPI necessary arguments have been collected. Go back and
550 // assign values that were unknown before the clang driver opt table was used.
551 ArchitectureSet AllArchs;
552 llvm::for_each(DriverOpts.Targets,
553 [&AllArchs](const auto &T) { AllArchs.set(T.first.Arch); });
554 auto assignDefaultLibAttrs = [&AllArchs](LibAttrs &Attrs) {
555 for (StringMapEntry<ArchitectureSet> &Entry : Attrs)
556 if (Entry.getValue().empty())
557 Entry.setValue(AllArchs);
558 };
559 assignDefaultLibAttrs(LinkerOpts.AllowableClients);
560 assignDefaultLibAttrs(LinkerOpts.ReexportedFrameworks);
561 assignDefaultLibAttrs(LinkerOpts.ReexportedLibraries);
562 assignDefaultLibAttrs(LinkerOpts.ReexportedLibraryPaths);
563 assignDefaultLibAttrs(LinkerOpts.RPaths);
564
565 /// Force cc1 options that should always be on.
566 FrontendArgs = {"-fsyntax-only", "-Wprivate-extern"};
567
568 /// Any unclaimed arguments should be handled by invoking the clang frontend.
569 for (const Arg *A : ArgList) {
570 if (A->isClaimed())
571 continue;
572 FrontendArgs.emplace_back(A->getSpelling());
573 llvm::copy(A->getValues(), std::back_inserter(FrontendArgs));
574 }
575}
576
577static const Regex Rule("(.+)/(.+)\\.framework/");
578static StringRef getFrameworkNameFromInstallName(StringRef InstallName) {
579 SmallVector<StringRef, 3> Match;
580 Rule.match(String: InstallName, Matches: &Match);
581 if (Match.empty())
582 return "";
583 return Match.back();
584}
585
586static Expected<std::unique_ptr<InterfaceFile>>
587getInterfaceFile(const StringRef Filename) {
588 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
589 MemoryBuffer::getFile(Filename);
590 if (auto Err = BufferOrErr.getError())
591 return errorCodeToError(std::move(Err));
592
593 auto Buffer = std::move(*BufferOrErr);
594 std::unique_ptr<InterfaceFile> IF;
595 switch (identify_magic(Buffer->getBuffer())) {
596 case file_magic::macho_dynamically_linked_shared_lib:
597 LLVM_FALLTHROUGH;
598 case file_magic::macho_dynamically_linked_shared_lib_stub:
599 LLVM_FALLTHROUGH;
600 case file_magic::macho_universal_binary:
601 return DylibReader::get(Buffer: Buffer->getMemBufferRef());
602 break;
603 case file_magic::tapi_file:
604 return TextAPIReader::get(InputBuffer: Buffer->getMemBufferRef());
605 default:
606 return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
607 "unsupported library file format");
608 }
609 llvm_unreachable("unexpected failure in getInterface");
610}
611
612std::pair<LibAttrs, ReexportedInterfaces> Options::getReexportedLibraries() {
613 LibAttrs Reexports;
614 ReexportedInterfaces ReexportIFs;
615 auto AccumulateReexports = [&](StringRef Path, const ArchitectureSet &Archs) {
616 auto ReexportIFOrErr = getInterfaceFile(Path);
617 if (!ReexportIFOrErr)
618 return false;
619 std::unique_ptr<InterfaceFile> Reexport = std::move(*ReexportIFOrErr);
620 StringRef InstallName = Reexport->getInstallName();
621 assert(!InstallName.empty() && "Parse error for install name");
622 Reexports.insert({InstallName, Archs});
623 ReexportIFs.emplace_back(std::move(*Reexport));
624 return true;
625 };
626
627 // Populate search paths by looking at user paths before system ones.
628 PathSeq FwkSearchPaths(FEOpts.FwkPaths.begin(), FEOpts.FwkPaths.end());
629 // FIXME: System framework paths need to reset if installapi is invoked with
630 // different platforms.
631 FwkSearchPaths.insert(FwkSearchPaths.end(), FEOpts.SystemFwkPaths.begin(),
632 FEOpts.SystemFwkPaths.end());
633
634 for (const StringMapEntry<ArchitectureSet> &Lib :
635 LinkerOpts.ReexportedLibraries) {
636 std::string Name = "lib" + Lib.getKey().str() + ".dylib";
637 std::string Path = findLibrary(Name, *FM, {}, LinkerOpts.LibPaths, {});
638 if (Path.empty()) {
639 Diags->Report(diag::err_cannot_find_reexport) << true << Lib.getKey();
640 return {};
641 }
642 if (DriverOpts.TraceLibraryLocation)
643 errs() << Path << "\n";
644
645 AccumulateReexports(Path, Lib.getValue());
646 }
647
648 for (const StringMapEntry<ArchitectureSet> &Lib :
649 LinkerOpts.ReexportedLibraryPaths)
650 AccumulateReexports(Lib.getKey(), Lib.getValue());
651
652 for (const StringMapEntry<ArchitectureSet> &Lib :
653 LinkerOpts.ReexportedFrameworks) {
654 std::string Name = (Lib.getKey() + ".framework/" + Lib.getKey()).str();
655 std::string Path = findLibrary(Name, *FM, FwkSearchPaths, {}, {});
656 if (Path.empty()) {
657 Diags->Report(diag::err_cannot_find_reexport) << false << Lib.getKey();
658 return {};
659 }
660 if (DriverOpts.TraceLibraryLocation)
661 errs() << Path << "\n";
662
663 AccumulateReexports(Path, Lib.getValue());
664 }
665
666 return {std::move(Reexports), std::move(ReexportIFs)};
667}
668
669InstallAPIContext Options::createContext() {
670 InstallAPIContext Ctx;
671 Ctx.FM = FM;
672 Ctx.Diags = Diags;
673
674 // InstallAPI requires two level namespacing.
675 Ctx.BA.TwoLevelNamespace = true;
676
677 Ctx.BA.InstallName = LinkerOpts.InstallName;
678 Ctx.BA.CurrentVersion = LinkerOpts.CurrentVersion;
679 Ctx.BA.CompatVersion = LinkerOpts.CompatVersion;
680 Ctx.BA.AppExtensionSafe = LinkerOpts.AppExtensionSafe;
681 Ctx.BA.ParentUmbrella = LinkerOpts.ParentUmbrella;
682 Ctx.BA.OSLibNotForSharedCache = LinkerOpts.OSLibNotForSharedCache;
683 Ctx.FT = DriverOpts.OutFT;
684 Ctx.OutputLoc = DriverOpts.OutputPath;
685 Ctx.LangMode = FEOpts.LangMode;
686
687 auto [Reexports, ReexportedIFs] = getReexportedLibraries();
688 if (Diags->hasErrorOccurred())
689 return Ctx;
690 Ctx.Reexports = Reexports;
691
692 // Collect symbols from alias lists.
693 AliasMap Aliases;
694 for (const StringRef ListPath : LinkerOpts.AliasLists) {
695 auto Buffer = FM->getBufferForFile(ListPath);
696 if (auto Err = Buffer.getError()) {
697 Diags->Report(diag::err_cannot_open_file) << ListPath << Err.message();
698 return Ctx;
699 }
700 Expected<AliasMap> Result = parseAliasList(Buffer.get());
701 if (!Result) {
702 Diags->Report(diag::err_cannot_read_alias_list)
703 << ListPath << toString(Result.takeError());
704 return Ctx;
705 }
706 Aliases.insert(Result.get().begin(), Result.get().end());
707 }
708
709 // Attempt to find umbrella headers by capturing framework name.
710 StringRef FrameworkName;
711 if (!LinkerOpts.IsDylib)
712 FrameworkName = getFrameworkNameFromInstallName(InstallName: LinkerOpts.InstallName);
713
714 // Process inputs.
715 for (const StringRef ListPath : DriverOpts.FileLists) {
716 auto Buffer = FM->getBufferForFile(ListPath);
717 if (auto Err = Buffer.getError()) {
718 Diags->Report(diag::err_cannot_open_file) << ListPath << Err.message();
719 return Ctx;
720 }
721 if (auto Err = FileListReader::loadHeaders(std::move(Buffer.get()),
722 Ctx.InputHeaders)) {
723 Diags->Report(diag::err_cannot_open_file) << ListPath << std::move(Err);
724 return Ctx;
725 }
726 }
727 // After initial input has been processed, add any extra headers.
728 auto HandleExtraHeaders = [&](PathSeq &Headers, HeaderType Type) -> bool {
729 assert(Type != HeaderType::Unknown && "Missing header type.");
730 for (const StringRef Path : Headers) {
731 if (!FM->getOptionalFileRef(Path)) {
732 Diags->Report(diag::err_no_such_header_file) << Path << (unsigned)Type;
733 return false;
734 }
735 SmallString<PATH_MAX> FullPath(Path);
736 FM->makeAbsolutePath(FullPath);
737
738 auto IncludeName = createIncludeHeaderName(FullPath);
739 Ctx.InputHeaders.emplace_back(
740 FullPath, Type, IncludeName.has_value() ? *IncludeName : "");
741 Ctx.InputHeaders.back().setExtra();
742 }
743 return true;
744 };
745
746 if (!HandleExtraHeaders(DriverOpts.ExtraPublicHeaders, HeaderType::Public) ||
747 !HandleExtraHeaders(DriverOpts.ExtraPrivateHeaders,
748 HeaderType::Private) ||
749 !HandleExtraHeaders(DriverOpts.ExtraProjectHeaders, HeaderType::Project))
750 return Ctx;
751
752 // After all headers have been added, consider excluded headers.
753 std::vector<std::unique_ptr<HeaderGlob>> ExcludedHeaderGlobs;
754 std::set<FileEntryRef> ExcludedHeaderFiles;
755 auto ParseGlobs = [&](const PathSeq &Paths, HeaderType Type) {
756 assert(Type != HeaderType::Unknown && "Missing header type.");
757 for (const StringRef Path : Paths) {
758 auto Glob = HeaderGlob::create(Path, Type);
759 if (Glob)
760 ExcludedHeaderGlobs.emplace_back(std::move(Glob.get()));
761 else {
762 consumeError(Glob.takeError());
763 if (auto File = FM->getFileRef(Path))
764 ExcludedHeaderFiles.emplace(*File);
765 else {
766 Diags->Report(diag::err_no_such_header_file)
767 << Path << (unsigned)Type;
768 return false;
769 }
770 }
771 }
772 return true;
773 };
774
775 if (!ParseGlobs(DriverOpts.ExcludePublicHeaders, HeaderType::Public) ||
776 !ParseGlobs(DriverOpts.ExcludePrivateHeaders, HeaderType::Private) ||
777 !ParseGlobs(DriverOpts.ExcludeProjectHeaders, HeaderType::Project))
778 return Ctx;
779
780 for (HeaderFile &Header : Ctx.InputHeaders) {
781 for (auto &Glob : ExcludedHeaderGlobs)
782 if (Glob->match(Header))
783 Header.setExcluded();
784 }
785 if (!ExcludedHeaderFiles.empty()) {
786 for (HeaderFile &Header : Ctx.InputHeaders) {
787 auto FileRef = FM->getFileRef(Header.getPath());
788 if (!FileRef)
789 continue;
790 if (ExcludedHeaderFiles.count(*FileRef))
791 Header.setExcluded();
792 }
793 }
794 // Report if glob was ignored.
795 for (const auto &Glob : ExcludedHeaderGlobs)
796 if (!Glob->didMatch())
797 Diags->Report(diag::warn_glob_did_not_match) << Glob->str();
798
799 // Mark any explicit or inferred umbrella headers. If one exists, move
800 // that to the beginning of the input headers.
801 auto MarkandMoveUmbrellaInHeaders = [&](llvm::Regex &Regex,
802 HeaderType Type) -> bool {
803 auto It = find_if(Ctx.InputHeaders, [&Regex, Type](const HeaderFile &H) {
804 return (H.getType() == Type) && Regex.match(String: H.getPath());
805 });
806
807 if (It == Ctx.InputHeaders.end())
808 return false;
809 It->setUmbrellaHeader();
810
811 // Because there can be an umbrella header per header type,
812 // find the first non umbrella header to swap position with.
813 auto BeginPos = find_if(Ctx.InputHeaders, [](const HeaderFile &H) {
814 return !H.isUmbrellaHeader();
815 });
816 if (BeginPos != Ctx.InputHeaders.end() && BeginPos < It)
817 std::swap(*BeginPos, *It);
818 return true;
819 };
820
821 auto FindUmbrellaHeader = [&](StringRef HeaderPath, HeaderType Type) -> bool {
822 assert(Type != HeaderType::Unknown && "Missing header type.");
823 if (!HeaderPath.empty()) {
824 auto EscapedString = Regex::escape(String: HeaderPath);
825 Regex UmbrellaRegex(EscapedString);
826 if (!MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type)) {
827 Diags->Report(diag::err_no_such_umbrella_header_file)
828 << HeaderPath << (unsigned)Type;
829 return false;
830 }
831 } else if (!FrameworkName.empty() && (Type != HeaderType::Project)) {
832 auto UmbrellaName = "/" + Regex::escape(String: FrameworkName);
833 if (Type == HeaderType::Public)
834 UmbrellaName += "\\.h";
835 else
836 UmbrellaName += "[_]?Private\\.h";
837 Regex UmbrellaRegex(UmbrellaName);
838 MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type);
839 }
840 return true;
841 };
842 if (!FindUmbrellaHeader(DriverOpts.PublicUmbrellaHeader,
843 HeaderType::Public) ||
844 !FindUmbrellaHeader(DriverOpts.PrivateUmbrellaHeader,
845 HeaderType::Private) ||
846 !FindUmbrellaHeader(DriverOpts.ProjectUmbrellaHeader,
847 HeaderType::Project))
848 return Ctx;
849
850 // Parse binary dylib and initialize verifier.
851 if (DriverOpts.DylibToVerify.empty()) {
852 Ctx.Verifier = std::make_unique<DylibVerifier>();
853 return Ctx;
854 }
855
856 auto Buffer = FM->getBufferForFile(DriverOpts.DylibToVerify);
857 if (auto Err = Buffer.getError()) {
858 Diags->Report(diag::err_cannot_open_file)
859 << DriverOpts.DylibToVerify << Err.message();
860 return Ctx;
861 }
862
863 DylibReader::ParseOption PO;
864 PO.Undefineds = false;
865 Expected<Records> Slices =
866 DylibReader::readFile((*Buffer)->getMemBufferRef(), PO);
867 if (auto Err = Slices.takeError()) {
868 Diags->Report(diag::err_cannot_open_file)
869 << DriverOpts.DylibToVerify << std::move(Err);
870 return Ctx;
871 }
872
873 Ctx.Verifier = std::make_unique<DylibVerifier>(
874 std::move(*Slices), std::move(ReexportedIFs), std::move(Aliases), Diags,
875 DriverOpts.VerifyMode, DriverOpts.Zippered, DriverOpts.Demangle,
876 DriverOpts.DSYMPath);
877 return Ctx;
878}
879
880} // namespace installapi
881} // namespace clang
882

source code of clang/tools/clang-installapi/Options.cpp