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/DirectoryScanner.h"
13#include "clang/InstallAPI/FileList.h"
14#include "clang/InstallAPI/HeaderFile.h"
15#include "clang/InstallAPI/InstallAPIDiagnostic.h"
16#include "llvm/BinaryFormat/Magic.h"
17#include "llvm/Support/JSON.h"
18#include "llvm/Support/Program.h"
19#include "llvm/Support/VirtualFileSystem.h"
20#include "llvm/TargetParser/Host.h"
21#include "llvm/TextAPI/DylibReader.h"
22#include "llvm/TextAPI/TextAPIError.h"
23#include "llvm/TextAPI/TextAPIReader.h"
24#include "llvm/TextAPI/TextAPIWriter.h"
25
26using namespace llvm;
27using namespace llvm::opt;
28using namespace llvm::MachO;
29
30namespace clang {
31namespace installapi {
32
33#define OPTTABLE_CODE
34#include "InstallAPIOpts.inc"
35
36namespace {
37
38/// \brief Create OptTable class for parsing actual command line arguments.
39class DriverOptTable : public opt::OptTable {
40public:
41 DriverOptTable() : OptTable(optionTables()) {}
42};
43
44} // end anonymous namespace.
45
46static llvm::opt::OptTable *createDriverOptTable() {
47 return new DriverOptTable();
48}
49
50/// Parse JSON input into argument list.
51///
52/* Expected input format.
53 * { "label" : ["-ClangArg1", "-ClangArg2"] }
54 */
55///
56/// Input is interpreted as "-Xlabel ClangArg1 -XLabel ClangArg2".
57static Expected<llvm::opt::InputArgList>
58getArgListFromJSON(const StringRef Input, llvm::opt::OptTable *Table,
59 std::vector<std::string> &Storage) {
60 using namespace json;
61 Expected<Value> ValOrErr = json::parse(JSON: Input);
62 if (!ValOrErr)
63 return ValOrErr.takeError();
64
65 const Object *Root = ValOrErr->getAsObject();
66 if (!Root)
67 return llvm::opt::InputArgList();
68
69 for (const auto &KV : *Root) {
70 const Array *ArgList = KV.getSecond().getAsArray();
71 std::string Label = "-X" + KV.getFirst().str();
72 if (!ArgList)
73 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat);
74 for (auto Arg : *ArgList) {
75 std::optional<StringRef> ArgStr = Arg.getAsString();
76 if (!ArgStr)
77 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat);
78 Storage.emplace_back(args&: Label);
79 Storage.emplace_back(args&: *ArgStr);
80 }
81 }
82
83 std::vector<const char *> CArgs(Storage.size());
84 for (StringRef Str : Storage)
85 CArgs.emplace_back(args: Str.data());
86
87 unsigned MissingArgIndex, MissingArgCount;
88 return Table->ParseArgs(Args: CArgs, MissingArgIndex, MissingArgCount);
89}
90
91bool Options::processDriverOptions(InputArgList &Args) {
92 // Handle inputs.
93 for (const StringRef Path : Args.getAllArgValues(Id: options::OPT_INPUT)) {
94 // Assume any input that is not a directory is a filelist.
95 // InstallAPI does not accept multiple directories, so retain the last one.
96 if (FM->getOptionalDirectoryRef(DirName: Path))
97 DriverOpts.InputDirectory = Path.str();
98 else
99 DriverOpts.FileLists.emplace_back(args: Path.str());
100 }
101
102 // Handle output.
103 SmallString<PATH_MAX> OutputPath;
104 if (auto *Arg = Args.getLastArg(Ids: options::OPT_o)) {
105 OutputPath = Arg->getValue();
106 if (OutputPath != "-")
107 FM->makeAbsolutePath(Path&: OutputPath);
108 DriverOpts.OutputPath = std::string(OutputPath);
109 }
110 if (DriverOpts.OutputPath.empty()) {
111 Diags->Report(DiagID: diag::err_no_output_file);
112 return false;
113 }
114
115 // Do basic error checking first for mixing -target and -arch options.
116 auto *ArgArch = Args.getLastArgNoClaim(Ids: options::OPT_arch);
117 auto *ArgTarget = Args.getLastArgNoClaim(Ids: options::OPT_target);
118 auto *ArgTargetVariant =
119 Args.getLastArgNoClaim(Ids: options::OPT_darwin_target_variant);
120 if (ArgArch && (ArgTarget || ArgTargetVariant)) {
121 Diags->Report(DiagID: clang::diag::err_drv_argument_not_allowed_with)
122 << ArgArch->getAsString(Args)
123 << (ArgTarget ? ArgTarget : ArgTargetVariant)->getAsString(Args);
124 return false;
125 }
126
127 auto *ArgMinTargetOS = Args.getLastArgNoClaim(Ids: options::OPT_mtargetos_EQ);
128 if ((ArgTarget || ArgTargetVariant) && ArgMinTargetOS) {
129 Diags->Report(DiagID: clang::diag::err_drv_cannot_mix_options)
130 << ArgTarget->getAsString(Args) << ArgMinTargetOS->getAsString(Args);
131 return false;
132 }
133
134 // Capture target triples first.
135 if (ArgTarget) {
136 for (const Arg *A : Args.filtered(Ids: options::OPT_target)) {
137 A->claim();
138 llvm::Triple TargetTriple(A->getValue());
139 Target TAPITarget = Target(TargetTriple);
140 if ((TAPITarget.Arch == AK_unknown) ||
141 (TAPITarget.Platform == PLATFORM_UNKNOWN)) {
142 Diags->Report(DiagID: clang::diag::err_drv_unsupported_opt_for_target)
143 << "installapi" << TargetTriple.str();
144 return false;
145 }
146 DriverOpts.Targets[TAPITarget] = TargetTriple;
147 }
148 }
149
150 // Capture target variants.
151 DriverOpts.Zippered = ArgTargetVariant != nullptr;
152 for (Arg *A : Args.filtered(Ids: options::OPT_darwin_target_variant)) {
153 A->claim();
154 Triple Variant(A->getValue());
155 if (Variant.getVendor() != Triple::Apple) {
156 Diags->Report(DiagID: diag::err_unsupported_vendor)
157 << Variant.getVendorName() << A->getAsString(Args);
158 return false;
159 }
160
161 switch (Variant.getOS()) {
162 default:
163 Diags->Report(DiagID: diag::err_unsupported_os)
164 << Variant.getOSName() << A->getAsString(Args);
165 return false;
166 case Triple::MacOSX:
167 case Triple::IOS:
168 break;
169 }
170
171 switch (Variant.getEnvironment()) {
172 default:
173 Diags->Report(DiagID: diag::err_unsupported_environment)
174 << Variant.getEnvironmentName() << A->getAsString(Args);
175 return false;
176 case Triple::UnknownEnvironment:
177 case Triple::MacABI:
178 break;
179 }
180
181 Target TAPIVariant(Variant);
182 // See if there is a matching --target option for this --target-variant
183 // option.
184 auto It = find_if(Range&: DriverOpts.Targets, P: [&](const auto &T) {
185 return (T.first.Arch == TAPIVariant.Arch) &&
186 (T.first.Platform != PlatformType::PLATFORM_UNKNOWN);
187 });
188
189 if (It == DriverOpts.Targets.end()) {
190 Diags->Report(DiagID: diag::err_no_matching_target) << Variant.str();
191 return false;
192 }
193
194 DriverOpts.Targets[TAPIVariant] = Variant;
195 }
196
197 DriverOpts.Verbose = Args.hasArgNoClaim(Ids: options::OPT_v);
198
199 return true;
200}
201
202bool Options::processInstallAPIXOptions(InputArgList &Args) {
203 for (arg_iterator It = Args.begin(), End = Args.end(); It != End; ++It) {
204 Arg *A = *It;
205 if (A->getOption().matches(ID: OPT_Xarch__)) {
206 if (!processXarchOption(Args, Curr: It))
207 return false;
208 continue;
209 } else if (A->getOption().matches(ID: OPT_Xplatform__)) {
210 if (!processXplatformOption(Args, Curr: It))
211 return false;
212 continue;
213 } else if (A->getOption().matches(ID: OPT_Xproject)) {
214 if (!processXprojectOption(Args, Curr: It))
215 return false;
216 continue;
217 } else if (!A->getOption().matches(ID: OPT_X__))
218 continue;
219
220 // Handle any user defined labels.
221 const StringRef Label = A->getValue(N: 0);
222
223 // Ban "public" and "private" labels.
224 if ((Label.lower() == "public") || (Label.lower() == "private")) {
225 Diags->Report(DiagID: diag::err_invalid_label) << Label;
226 return false;
227 }
228
229 auto NextIt = std::next(x: It);
230 if (NextIt == End) {
231 Diags->Report(DiagID: clang::diag::err_drv_missing_argument)
232 << A->getAsString(Args) << 1;
233 return false;
234 }
235 Arg *NextA = *NextIt;
236 switch ((ID)NextA->getOption().getID()) {
237 case OPT_D:
238 case OPT_U:
239 break;
240 default:
241 Diags->Report(DiagID: clang::diag::err_drv_argument_not_allowed_with)
242 << A->getAsString(Args) << NextA->getAsString(Args);
243 return false;
244 }
245 const StringRef ASpelling = NextA->getSpelling();
246 const auto &AValues = NextA->getValues();
247 auto &UniqueArgs = FEOpts.UniqueArgs[Label];
248 if (AValues.empty())
249 UniqueArgs.emplace_back(args: ASpelling.str());
250 else
251 for (const StringRef Val : AValues)
252 UniqueArgs.emplace_back(args: (ASpelling + Val).str());
253
254 A->claim();
255 NextA->claim();
256 }
257
258 return true;
259}
260
261bool Options::processXplatformOption(InputArgList &Args, arg_iterator Curr) {
262 Arg *A = *Curr;
263
264 PlatformType Platform = getPlatformFromName(Name: A->getValue(N: 0));
265 if (Platform == PLATFORM_UNKNOWN) {
266 Diags->Report(DiagID: diag::err_unsupported_os)
267 << getPlatformName(Platform) << A->getAsString(Args);
268 return false;
269 }
270 auto NextIt = std::next(x: Curr);
271 if (NextIt == Args.end()) {
272 Diags->Report(DiagID: diag::err_drv_missing_argument) << A->getAsString(Args) << 1;
273 return false;
274 }
275
276 Arg *NextA = *NextIt;
277 switch ((ID)NextA->getOption().getID()) {
278 case OPT_iframework:
279 FEOpts.SystemFwkPaths.emplace_back(args: NextA->getValue(), args&: Platform);
280 break;
281 default:
282 Diags->Report(DiagID: diag::err_drv_invalid_argument_to_option)
283 << A->getAsString(Args) << NextA->getAsString(Args);
284 return false;
285 }
286
287 A->claim();
288 NextA->claim();
289
290 return true;
291}
292
293bool Options::processXprojectOption(InputArgList &Args, arg_iterator Curr) {
294 Arg *A = *Curr;
295 auto NextIt = std::next(x: Curr);
296 if (NextIt == Args.end()) {
297 Diags->Report(DiagID: diag::err_drv_missing_argument) << A->getAsString(Args) << 1;
298 return false;
299 }
300
301 Arg *NextA = *NextIt;
302 switch ((ID)NextA->getOption().getID()) {
303 case OPT_fobjc_arc:
304 case OPT_fmodules:
305 case OPT_fmodules_cache_path:
306 case OPT_include_:
307 case OPT_fvisibility_EQ:
308 break;
309 default:
310 Diags->Report(DiagID: diag::err_drv_argument_not_allowed_with)
311 << A->getAsString(Args) << NextA->getAsString(Args);
312 return false;
313 }
314
315 std::string ArgString = NextA->getSpelling().str();
316 for (const StringRef Val : NextA->getValues())
317 ArgString += Val.str();
318
319 ProjectLevelArgs.push_back(x: ArgString);
320 A->claim();
321 NextA->claim();
322
323 return true;
324}
325
326bool Options::processXarchOption(InputArgList &Args, arg_iterator Curr) {
327 Arg *CurrArg = *Curr;
328 Architecture Arch = getArchitectureFromName(Name: CurrArg->getValue(N: 0));
329 if (Arch == AK_unknown) {
330 Diags->Report(DiagID: diag::err_drv_invalid_arch_name)
331 << CurrArg->getAsString(Args);
332 return false;
333 }
334
335 auto NextIt = std::next(x: Curr);
336 if (NextIt == Args.end()) {
337 Diags->Report(DiagID: diag::err_drv_missing_argument)
338 << CurrArg->getAsString(Args) << 1;
339 return false;
340 }
341
342 // InstallAPI has a limited understanding of supported Xarch options.
343 // Currently this is restricted to linker inputs.
344 const Arg *NextArg = *NextIt;
345 switch (NextArg->getOption().getID()) {
346 case OPT_allowable_client:
347 case OPT_reexport_l:
348 case OPT_reexport_framework:
349 case OPT_reexport_library:
350 case OPT_rpath:
351 break;
352 default:
353 Diags->Report(DiagID: diag::err_drv_invalid_argument_to_option)
354 << NextArg->getAsString(Args) << CurrArg->getAsString(Args);
355 return false;
356 }
357
358 ArgToArchMap[NextArg] = Arch;
359 CurrArg->claim();
360
361 return true;
362}
363
364bool Options::processOptionList(InputArgList &Args,
365 llvm::opt::OptTable *Table) {
366 Arg *A = Args.getLastArg(Ids: OPT_option_list);
367 if (!A)
368 return true;
369
370 const StringRef Path = A->getValue(N: 0);
371 auto InputOrErr = FM->getBufferForFile(Filename: Path);
372 if (auto Err = InputOrErr.getError()) {
373 Diags->Report(DiagID: diag::err_cannot_open_file) << Path << Err.message();
374 return false;
375 }
376 // Backing storage referenced for argument processing.
377 std::vector<std::string> Storage;
378 auto ArgsOrErr =
379 getArgListFromJSON(Input: (*InputOrErr)->getBuffer(), Table, Storage);
380
381 if (auto Err = ArgsOrErr.takeError()) {
382 Diags->Report(DiagID: diag::err_cannot_read_input_list)
383 << "option" << Path << toString(E: std::move(Err));
384 return false;
385 }
386 return processInstallAPIXOptions(Args&: *ArgsOrErr);
387}
388
389bool Options::processLinkerOptions(InputArgList &Args) {
390 // Handle required arguments.
391 if (const Arg *A = Args.getLastArg(Ids: options::OPT_install__name))
392 LinkerOpts.InstallName = A->getValue();
393 if (LinkerOpts.InstallName.empty()) {
394 Diags->Report(DiagID: diag::err_no_install_name);
395 return false;
396 }
397
398 // Defaulted or optional arguments.
399 if (auto *Arg = Args.getLastArg(Ids: options::OPT_current__version))
400 LinkerOpts.CurrentVersion.parse64(Str: Arg->getValue());
401
402 if (auto *Arg = Args.getLastArg(Ids: options::OPT_compatibility__version))
403 LinkerOpts.CompatVersion.parse64(Str: Arg->getValue());
404
405 if (auto *Arg = Args.getLastArg(Ids: options::OPT_compatibility__version))
406 LinkerOpts.CompatVersion.parse64(Str: Arg->getValue());
407
408 if (auto *Arg = Args.getLastArg(Ids: options::OPT_umbrella))
409 LinkerOpts.ParentUmbrella = Arg->getValue();
410
411 LinkerOpts.IsDylib = Args.hasArg(Ids: options::OPT_dynamiclib);
412
413 for (auto *Arg : Args.filtered(Ids: options::OPT_alias_list)) {
414 LinkerOpts.AliasLists.emplace_back(args: Arg->getValue());
415 Arg->claim();
416 }
417
418 LinkerOpts.AppExtensionSafe =
419 Args.hasFlag(Pos: options::OPT_fapplication_extension,
420 Neg: options::OPT_fno_application_extension,
421 /*Default=*/LinkerOpts.AppExtensionSafe);
422
423 if (::getenv(name: "LD_NO_ENCRYPT") != nullptr)
424 LinkerOpts.AppExtensionSafe = true;
425
426 if (::getenv(name: "LD_APPLICATION_EXTENSION_SAFE") != nullptr)
427 LinkerOpts.AppExtensionSafe = true;
428
429 // Capture library paths.
430 PathSeq LibraryPaths;
431 for (const Arg *A : Args.filtered(Ids: options::OPT_L)) {
432 LibraryPaths.emplace_back(args: A->getValue());
433 A->claim();
434 }
435
436 if (!LibraryPaths.empty())
437 LinkerOpts.LibPaths = std::move(LibraryPaths);
438
439 return true;
440}
441
442// NOTE: Do not claim any arguments, as they will be passed along for CC1
443// invocations.
444bool Options::processFrontendOptions(InputArgList &Args) {
445 // Capture language mode.
446 if (auto *A = Args.getLastArgNoClaim(Ids: options::OPT_x)) {
447 FEOpts.LangMode = llvm::StringSwitch<clang::Language>(A->getValue())
448 .Case(S: "c", Value: clang::Language::C)
449 .Case(S: "c++", Value: clang::Language::CXX)
450 .Case(S: "objective-c", Value: clang::Language::ObjC)
451 .Case(S: "objective-c++", Value: clang::Language::ObjCXX)
452 .Default(Value: clang::Language::Unknown);
453
454 if (FEOpts.LangMode == clang::Language::Unknown) {
455 Diags->Report(DiagID: clang::diag::err_drv_invalid_value)
456 << A->getAsString(Args) << A->getValue();
457 return false;
458 }
459 }
460 for (auto *A : Args.filtered(Ids: options::OPT_ObjC, Ids: options::OPT_ObjCXX)) {
461 if (A->getOption().matches(ID: options::OPT_ObjC))
462 FEOpts.LangMode = clang::Language::ObjC;
463 else
464 FEOpts.LangMode = clang::Language::ObjCXX;
465 }
466
467 // Capture Sysroot.
468 if (const Arg *A = Args.getLastArgNoClaim(Ids: options::OPT_isysroot)) {
469 SmallString<PATH_MAX> Path(A->getValue());
470 FM->makeAbsolutePath(Path);
471 if (!FM->getOptionalDirectoryRef(DirName: Path)) {
472 Diags->Report(DiagID: diag::err_missing_sysroot) << Path;
473 return false;
474 }
475 FEOpts.ISysroot = std::string(Path);
476 } else if (FEOpts.ISysroot.empty()) {
477 // Mirror CLANG and obtain the isysroot from the SDKROOT environment
478 // variable, if it wasn't defined by the command line.
479 if (auto *Env = ::getenv(name: "SDKROOT")) {
480 if (StringRef(Env) != "/" && llvm::sys::path::is_absolute(path: Env) &&
481 FM->getOptionalFileRef(Filename: Env))
482 FEOpts.ISysroot = Env;
483 }
484 }
485
486 // Capture system frameworks for all platforms.
487 for (const Arg *A : Args.filtered(Ids: options::OPT_iframework))
488 FEOpts.SystemFwkPaths.emplace_back(args: A->getValue(),
489 args: std::optional<PlatformType>{});
490
491 // Capture framework paths.
492 PathSeq FrameworkPaths;
493 for (const Arg *A : Args.filtered(Ids: options::OPT_F))
494 FrameworkPaths.emplace_back(args: A->getValue());
495
496 if (!FrameworkPaths.empty())
497 FEOpts.FwkPaths = std::move(FrameworkPaths);
498
499 // Add default framework/library paths.
500 PathSeq DefaultLibraryPaths = {"/usr/lib", "/usr/local/lib"};
501 PathSeq DefaultFrameworkPaths = {"/Library/Frameworks",
502 "/System/Library/Frameworks"};
503
504 for (const StringRef LibPath : DefaultLibraryPaths) {
505 SmallString<PATH_MAX> Path(FEOpts.ISysroot);
506 sys::path::append(path&: Path, a: LibPath);
507 LinkerOpts.LibPaths.emplace_back(args: Path.str());
508 }
509 for (const StringRef FwkPath : DefaultFrameworkPaths) {
510 SmallString<PATH_MAX> Path(FEOpts.ISysroot);
511 sys::path::append(path&: Path, a: FwkPath);
512 FEOpts.SystemFwkPaths.emplace_back(args: Path.str(),
513 args: std::optional<PlatformType>{});
514 }
515
516 return true;
517}
518
519bool Options::addFilePaths(InputArgList &Args, PathSeq &Headers,
520 OptSpecifier ID) {
521 for (const StringRef Path : Args.getAllArgValues(Id: ID)) {
522 if ((bool)FM->getOptionalDirectoryRef(DirName: Path, /*CacheFailure=*/false)) {
523 auto InputHeadersOrErr = enumerateFiles(FM&: *FM, Directory: Path);
524 if (!InputHeadersOrErr) {
525 Diags->Report(DiagID: diag::err_cannot_open_file)
526 << Path << toString(E: InputHeadersOrErr.takeError());
527 return false;
528 }
529 // Sort headers to ensure deterministic behavior.
530 sort(C&: *InputHeadersOrErr);
531 for (StringRef H : *InputHeadersOrErr)
532 Headers.emplace_back(args: std::move(H));
533 } else
534 Headers.emplace_back(args: Path);
535 }
536 return true;
537}
538
539std::vector<const char *>
540Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) {
541 std::unique_ptr<llvm::opt::OptTable> Table;
542 Table.reset(p: createDriverOptTable());
543
544 unsigned MissingArgIndex, MissingArgCount;
545 auto ParsedArgs = Table->ParseArgs(Args: Args.slice(N: 1), MissingArgIndex,
546 MissingArgCount, FlagsToInclude: Visibility());
547
548 // Capture InstallAPI only driver options.
549 if (!processInstallAPIXOptions(Args&: ParsedArgs))
550 return {};
551
552 if (!processOptionList(Args&: ParsedArgs, Table: Table.get()))
553 return {};
554
555 DriverOpts.Demangle = ParsedArgs.hasArg(Ids: OPT_demangle);
556
557 if (auto *A = ParsedArgs.getLastArg(Ids: OPT_filetype)) {
558 DriverOpts.OutFT = TextAPIWriter::parseFileType(FT: A->getValue());
559 if (DriverOpts.OutFT == FileType::Invalid) {
560 Diags->Report(DiagID: clang::diag::err_drv_invalid_value)
561 << A->getAsString(Args: ParsedArgs) << A->getValue();
562 return {};
563 }
564 }
565
566 if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_verify_mode_EQ)) {
567 DriverOpts.VerifyMode =
568 StringSwitch<VerificationMode>(A->getValue())
569 .Case(S: "ErrorsOnly", Value: VerificationMode::ErrorsOnly)
570 .Case(S: "ErrorsAndWarnings", Value: VerificationMode::ErrorsAndWarnings)
571 .Case(S: "Pedantic", Value: VerificationMode::Pedantic)
572 .Default(Value: VerificationMode::Invalid);
573
574 if (DriverOpts.VerifyMode == VerificationMode::Invalid) {
575 Diags->Report(DiagID: clang::diag::err_drv_invalid_value)
576 << A->getAsString(Args: ParsedArgs) << A->getValue();
577 return {};
578 }
579 }
580
581 if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_verify_against))
582 DriverOpts.DylibToVerify = A->getValue();
583
584 if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_dsym))
585 DriverOpts.DSYMPath = A->getValue();
586
587 DriverOpts.TraceLibraryLocation = ParsedArgs.hasArg(Ids: OPT_t);
588
589 // Linker options not handled by clang driver.
590 LinkerOpts.OSLibNotForSharedCache =
591 ParsedArgs.hasArg(Ids: OPT_not_for_dyld_shared_cache);
592
593 for (const Arg *A : ParsedArgs.filtered(Ids: OPT_allowable_client)) {
594 auto It = ArgToArchMap.find(Val: A);
595 LinkerOpts.AllowableClients.getArchSet(Attr: A->getValue()) =
596 It != ArgToArchMap.end() ? It->second : ArchitectureSet();
597 A->claim();
598 }
599
600 for (const Arg *A : ParsedArgs.filtered(Ids: OPT_reexport_l)) {
601 auto It = ArgToArchMap.find(Val: A);
602 LinkerOpts.ReexportedLibraries.getArchSet(Attr: A->getValue()) =
603 It != ArgToArchMap.end() ? It->second : ArchitectureSet();
604 A->claim();
605 }
606
607 for (const Arg *A : ParsedArgs.filtered(Ids: OPT_reexport_library)) {
608 auto It = ArgToArchMap.find(Val: A);
609 LinkerOpts.ReexportedLibraryPaths.getArchSet(Attr: A->getValue()) =
610 It != ArgToArchMap.end() ? It->second : ArchitectureSet();
611 A->claim();
612 }
613
614 for (const Arg *A : ParsedArgs.filtered(Ids: OPT_reexport_framework)) {
615 auto It = ArgToArchMap.find(Val: A);
616 LinkerOpts.ReexportedFrameworks.getArchSet(Attr: A->getValue()) =
617 It != ArgToArchMap.end() ? It->second : ArchitectureSet();
618 A->claim();
619 }
620
621 for (const Arg *A : ParsedArgs.filtered(Ids: OPT_rpath)) {
622 auto It = ArgToArchMap.find(Val: A);
623 LinkerOpts.RPaths.getArchSet(Attr: A->getValue()) =
624 It != ArgToArchMap.end() ? It->second : ArchitectureSet();
625 A->claim();
626 }
627
628 // Handle exclude & extra header directories or files.
629 auto handleAdditionalInputArgs = [&](PathSeq &Headers,
630 clang::installapi::ID OptID) {
631 if (ParsedArgs.hasArgNoClaim(Ids: OptID))
632 Headers.clear();
633 return addFilePaths(Args&: ParsedArgs, Headers, ID: OptID);
634 };
635
636 if (!handleAdditionalInputArgs(DriverOpts.ExtraPublicHeaders,
637 OPT_extra_public_header))
638 return {};
639
640 if (!handleAdditionalInputArgs(DriverOpts.ExtraPrivateHeaders,
641 OPT_extra_private_header))
642 return {};
643 if (!handleAdditionalInputArgs(DriverOpts.ExtraProjectHeaders,
644 OPT_extra_project_header))
645 return {};
646
647 if (!handleAdditionalInputArgs(DriverOpts.ExcludePublicHeaders,
648 OPT_exclude_public_header))
649 return {};
650 if (!handleAdditionalInputArgs(DriverOpts.ExcludePrivateHeaders,
651 OPT_exclude_private_header))
652 return {};
653 if (!handleAdditionalInputArgs(DriverOpts.ExcludeProjectHeaders,
654 OPT_exclude_project_header))
655 return {};
656
657 // Handle umbrella headers.
658 if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_public_umbrella_header))
659 DriverOpts.PublicUmbrellaHeader = A->getValue();
660
661 if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_private_umbrella_header))
662 DriverOpts.PrivateUmbrellaHeader = A->getValue();
663
664 if (const Arg *A = ParsedArgs.getLastArg(Ids: OPT_project_umbrella_header))
665 DriverOpts.ProjectUmbrellaHeader = A->getValue();
666
667 /// Any unclaimed arguments should be forwarded to the clang driver.
668 std::vector<const char *> ClangDriverArgs(ParsedArgs.size());
669 for (const Arg *A : ParsedArgs) {
670 if (A->isClaimed())
671 continue;
672 // Forward along unclaimed but overlapping arguments to the clang driver.
673 if (A->getOption().getID() > (unsigned)OPT_UNKNOWN) {
674 ClangDriverArgs.push_back(x: A->getSpelling().data());
675 } else
676 llvm::append_range(C&: ClangDriverArgs, R: A->getValues());
677 }
678 return ClangDriverArgs;
679}
680
681Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
682 ArrayRef<const char *> Args, const StringRef ProgName)
683 : Diags(&Diag), FM(FM) {
684
685 // First process InstallAPI specific options.
686 auto DriverArgs = processAndFilterOutInstallAPIOptions(Args);
687 if (Diags->hasErrorOccurred())
688 return;
689
690 // Set up driver to parse remaining input arguments.
691 clang::driver::Driver Driver(ProgName, llvm::sys::getDefaultTargetTriple(),
692 *Diags, "clang installapi tool");
693 auto TargetAndMode =
694 clang::driver::ToolChain::getTargetAndModeFromProgramName(ProgName);
695 Driver.setTargetAndMode(TargetAndMode);
696 bool HasError = false;
697 llvm::opt::InputArgList ArgList =
698 Driver.ParseArgStrings(Args: DriverArgs, /*UseDriverMode=*/true, ContainsError&: HasError);
699 if (HasError)
700 return;
701 Driver.setCheckInputsExist(false);
702
703 if (!processDriverOptions(Args&: ArgList))
704 return;
705
706 if (!processLinkerOptions(Args&: ArgList))
707 return;
708
709 if (!processFrontendOptions(Args&: ArgList))
710 return;
711
712 // After all InstallAPI necessary arguments have been collected. Go back and
713 // assign values that were unknown before the clang driver opt table was used.
714 ArchitectureSet AllArchs;
715 for (const auto &T : DriverOpts.Targets)
716 AllArchs.set(T.first.Arch);
717 auto assignDefaultLibAttrs = [&AllArchs](LibAttrs &Attrs) {
718 for (auto &[_, Archs] : Attrs.get())
719 if (Archs.empty())
720 Archs = AllArchs;
721 };
722 assignDefaultLibAttrs(LinkerOpts.AllowableClients);
723 assignDefaultLibAttrs(LinkerOpts.ReexportedFrameworks);
724 assignDefaultLibAttrs(LinkerOpts.ReexportedLibraries);
725 assignDefaultLibAttrs(LinkerOpts.ReexportedLibraryPaths);
726 assignDefaultLibAttrs(LinkerOpts.RPaths);
727
728 /// Force cc1 options that should always be on.
729 FrontendArgs = {"-fsyntax-only", "-Wprivate-extern"};
730
731 /// Any unclaimed arguments should be handled by invoking the clang frontend.
732 for (const Arg *A : ArgList) {
733 if (A->isClaimed())
734 continue;
735 FrontendArgs.emplace_back(args: A->getSpelling());
736 llvm::append_range(C&: FrontendArgs, R: A->getValues());
737 }
738}
739
740static Expected<std::unique_ptr<InterfaceFile>>
741getInterfaceFile(const StringRef Filename) {
742 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
743 MemoryBuffer::getFile(Filename);
744 if (auto Err = BufferOrErr.getError())
745 return errorCodeToError(EC: std::move(Err));
746
747 auto Buffer = std::move(*BufferOrErr);
748 switch (identify_magic(magic: Buffer->getBuffer())) {
749 case file_magic::macho_dynamically_linked_shared_lib:
750 case file_magic::macho_dynamically_linked_shared_lib_stub:
751 case file_magic::macho_universal_binary:
752 return DylibReader::get(Buffer: Buffer->getMemBufferRef());
753 break;
754 case file_magic::tapi_file:
755 return TextAPIReader::get(InputBuffer: Buffer->getMemBufferRef());
756 default:
757 return make_error<TextAPIError>(Args: TextAPIErrorCode::InvalidInputFormat,
758 Args: "unsupported library file format");
759 }
760 llvm_unreachable("unexpected failure in getInterface");
761}
762
763std::pair<LibAttrs, ReexportedInterfaces> Options::getReexportedLibraries() {
764 LibAttrs Reexports;
765 ReexportedInterfaces ReexportIFs;
766 auto AccumulateReexports = [&](StringRef Path, const ArchitectureSet &Archs) {
767 auto ReexportIFOrErr = getInterfaceFile(Filename: Path);
768 if (!ReexportIFOrErr)
769 return false;
770 std::unique_ptr<InterfaceFile> Reexport = std::move(*ReexportIFOrErr);
771 StringRef InstallName = Reexport->getInstallName();
772 assert(!InstallName.empty() && "Parse error for install name");
773 Reexports.getArchSet(Attr: InstallName) = Archs;
774 ReexportIFs.emplace_back(Args: std::move(*Reexport));
775 return true;
776 };
777
778 PlatformSet Platforms;
779 for (const auto &T : DriverOpts.Targets)
780 Platforms.insert(V: T.first.Platform);
781 // Populate search paths by looking at user paths before system ones.
782 PathSeq FwkSearchPaths(FEOpts.FwkPaths.begin(), FEOpts.FwkPaths.end());
783 for (const PlatformType P : Platforms) {
784 PathSeq PlatformSearchPaths = getPathsForPlatform(Paths: FEOpts.SystemFwkPaths, Platform: P);
785 llvm::append_range(C&: FwkSearchPaths, R&: PlatformSearchPaths);
786 for (const auto &[Lib, Archs] : LinkerOpts.ReexportedFrameworks.get()) {
787 std::string Name = (Lib + ".framework/" + Lib);
788 std::string Path = findLibrary(InstallName: Name, FM&: *FM, FrameworkSearchPaths: FwkSearchPaths, LibrarySearchPaths: {}, SearchPaths: {});
789 if (Path.empty()) {
790 Diags->Report(DiagID: diag::err_cannot_find_reexport) << false << Lib;
791 return {};
792 }
793 if (DriverOpts.TraceLibraryLocation)
794 errs() << Path << "\n";
795
796 AccumulateReexports(Path, Archs);
797 }
798 FwkSearchPaths.resize(new_size: FwkSearchPaths.size() - PlatformSearchPaths.size());
799 }
800
801 for (const auto &[Lib, Archs] : LinkerOpts.ReexportedLibraries.get()) {
802 std::string Name = "lib" + Lib + ".dylib";
803 std::string Path = findLibrary(InstallName: Name, FM&: *FM, FrameworkSearchPaths: {}, LibrarySearchPaths: LinkerOpts.LibPaths, SearchPaths: {});
804 if (Path.empty()) {
805 Diags->Report(DiagID: diag::err_cannot_find_reexport) << true << Lib;
806 return {};
807 }
808 if (DriverOpts.TraceLibraryLocation)
809 errs() << Path << "\n";
810
811 AccumulateReexports(Path, Archs);
812 }
813
814 for (const auto &[Lib, Archs] : LinkerOpts.ReexportedLibraryPaths.get())
815 AccumulateReexports(Lib, Archs);
816
817 return {std::move(Reexports), std::move(ReexportIFs)};
818}
819
820InstallAPIContext Options::createContext() {
821 InstallAPIContext Ctx;
822 Ctx.FM = FM;
823 Ctx.Diags = Diags;
824
825 // InstallAPI requires two level namespacing.
826 Ctx.BA.TwoLevelNamespace = true;
827
828 Ctx.BA.InstallName = LinkerOpts.InstallName;
829 Ctx.BA.CurrentVersion = LinkerOpts.CurrentVersion;
830 Ctx.BA.CompatVersion = LinkerOpts.CompatVersion;
831 Ctx.BA.AppExtensionSafe = LinkerOpts.AppExtensionSafe;
832 Ctx.BA.ParentUmbrella = LinkerOpts.ParentUmbrella;
833 Ctx.BA.OSLibNotForSharedCache = LinkerOpts.OSLibNotForSharedCache;
834 Ctx.FT = DriverOpts.OutFT;
835 Ctx.OutputLoc = DriverOpts.OutputPath;
836 Ctx.LangMode = FEOpts.LangMode;
837
838 auto [Reexports, ReexportedIFs] = getReexportedLibraries();
839 if (Diags->hasErrorOccurred())
840 return Ctx;
841 Ctx.Reexports = Reexports;
842
843 // Collect symbols from alias lists.
844 AliasMap Aliases;
845 for (const StringRef ListPath : LinkerOpts.AliasLists) {
846 auto Buffer = FM->getBufferForFile(Filename: ListPath);
847 if (auto Err = Buffer.getError()) {
848 Diags->Report(DiagID: diag::err_cannot_open_file) << ListPath << Err.message();
849 return Ctx;
850 }
851 Expected<AliasMap> Result = parseAliasList(Buffer&: Buffer.get());
852 if (!Result) {
853 Diags->Report(DiagID: diag::err_cannot_read_input_list)
854 << "symbol alias" << ListPath << toString(E: Result.takeError());
855 return Ctx;
856 }
857 Aliases.insert(first: Result.get().begin(), last: Result.get().end());
858 }
859
860 // Attempt to find umbrella headers by capturing framework name.
861 StringRef FrameworkName;
862 if (!LinkerOpts.IsDylib)
863 FrameworkName =
864 Library::getFrameworkNameFromInstallName(InstallName: LinkerOpts.InstallName);
865
866 /// Process inputs headers.
867 // 1. For headers discovered by directory scanning, sort them.
868 // 2. For headers discovered by filelist, respect ordering.
869 // 3. Append extra headers and mark any excluded headers.
870 // 4. Finally, surface up umbrella headers to top of the list.
871 if (!DriverOpts.InputDirectory.empty()) {
872 DirectoryScanner Scanner(*FM, LinkerOpts.IsDylib
873 ? ScanMode::ScanDylibs
874 : ScanMode::ScanFrameworks);
875 SmallString<PATH_MAX> NormalizedPath(DriverOpts.InputDirectory);
876 FM->getVirtualFileSystem().makeAbsolute(Path&: NormalizedPath);
877 sys::path::remove_dots(path&: NormalizedPath, /*remove_dot_dot=*/true);
878 if (llvm::Error Err = Scanner.scan(Directory: NormalizedPath)) {
879 Diags->Report(DiagID: diag::err_directory_scanning)
880 << DriverOpts.InputDirectory << std::move(Err);
881 return Ctx;
882 }
883 std::vector<Library> InputLibraries = Scanner.takeLibraries();
884 if (InputLibraries.size() > 1) {
885 Diags->Report(DiagID: diag::err_more_than_one_library);
886 return Ctx;
887 }
888 llvm::append_range(C&: Ctx.InputHeaders,
889 R: DirectoryScanner::getHeaders(Libraries: InputLibraries));
890 llvm::stable_sort(Range&: Ctx.InputHeaders);
891 }
892
893 for (const StringRef ListPath : DriverOpts.FileLists) {
894 auto Buffer = FM->getBufferForFile(Filename: ListPath);
895 if (auto Err = Buffer.getError()) {
896 Diags->Report(DiagID: diag::err_cannot_open_file) << ListPath << Err.message();
897 return Ctx;
898 }
899 if (auto Err = FileListReader::loadHeaders(InputBuffer: std::move(Buffer.get()),
900 Destination&: Ctx.InputHeaders, FM)) {
901 Diags->Report(DiagID: diag::err_cannot_read_input_list)
902 << "header file" << ListPath << std::move(Err);
903 return Ctx;
904 }
905 }
906 // After initial input has been processed, add any extra headers.
907 auto HandleExtraHeaders = [&](PathSeq &Headers, HeaderType Type) -> bool {
908 assert(Type != HeaderType::Unknown && "Missing header type.");
909 for (const StringRef Path : Headers) {
910 if (!FM->getOptionalFileRef(Filename: Path)) {
911 Diags->Report(DiagID: diag::err_no_such_header_file) << Path << (unsigned)Type;
912 return false;
913 }
914 SmallString<PATH_MAX> FullPath(Path);
915 FM->makeAbsolutePath(Path&: FullPath);
916
917 auto IncludeName = createIncludeHeaderName(FullPath);
918 Ctx.InputHeaders.emplace_back(
919 args&: FullPath, args&: Type, args: IncludeName.has_value() ? *IncludeName : "");
920 Ctx.InputHeaders.back().setExtra();
921 }
922 return true;
923 };
924
925 if (!HandleExtraHeaders(DriverOpts.ExtraPublicHeaders, HeaderType::Public) ||
926 !HandleExtraHeaders(DriverOpts.ExtraPrivateHeaders,
927 HeaderType::Private) ||
928 !HandleExtraHeaders(DriverOpts.ExtraProjectHeaders, HeaderType::Project))
929 return Ctx;
930
931 // After all headers have been added, consider excluded headers.
932 std::vector<std::unique_ptr<HeaderGlob>> ExcludedHeaderGlobs;
933 std::set<FileEntryRef> ExcludedHeaderFiles;
934 auto ParseGlobs = [&](const PathSeq &Paths, HeaderType Type) {
935 assert(Type != HeaderType::Unknown && "Missing header type.");
936 for (const StringRef Path : Paths) {
937 auto Glob = HeaderGlob::create(GlobString: Path, Type);
938 if (Glob)
939 ExcludedHeaderGlobs.emplace_back(args: std::move(Glob.get()));
940 else {
941 consumeError(Err: Glob.takeError());
942 if (auto File = FM->getFileRef(Filename: Path))
943 ExcludedHeaderFiles.emplace(args&: *File);
944 else {
945 Diags->Report(DiagID: diag::err_no_such_header_file)
946 << Path << (unsigned)Type;
947 return false;
948 }
949 }
950 }
951 return true;
952 };
953
954 if (!ParseGlobs(DriverOpts.ExcludePublicHeaders, HeaderType::Public) ||
955 !ParseGlobs(DriverOpts.ExcludePrivateHeaders, HeaderType::Private) ||
956 !ParseGlobs(DriverOpts.ExcludeProjectHeaders, HeaderType::Project))
957 return Ctx;
958
959 for (HeaderFile &Header : Ctx.InputHeaders) {
960 for (auto &Glob : ExcludedHeaderGlobs)
961 if (Glob->match(Header))
962 Header.setExcluded();
963 }
964 if (!ExcludedHeaderFiles.empty()) {
965 for (HeaderFile &Header : Ctx.InputHeaders) {
966 auto FileRef = FM->getFileRef(Filename: Header.getPath());
967 if (!FileRef)
968 continue;
969 if (ExcludedHeaderFiles.count(x: *FileRef))
970 Header.setExcluded();
971 }
972 }
973 // Report if glob was ignored.
974 for (const auto &Glob : ExcludedHeaderGlobs)
975 if (!Glob->didMatch())
976 Diags->Report(DiagID: diag::warn_glob_did_not_match) << Glob->str();
977
978 // Mark any explicit or inferred umbrella headers. If one exists, move
979 // that to the beginning of the input headers.
980 auto MarkandMoveUmbrellaInHeaders = [&](llvm::Regex &Regex,
981 HeaderType Type) -> bool {
982 auto It = find_if(Range&: Ctx.InputHeaders, P: [&Regex, Type](const HeaderFile &H) {
983 return (H.getType() == Type) && Regex.match(String: H.getPath());
984 });
985
986 if (It == Ctx.InputHeaders.end())
987 return false;
988 It->setUmbrellaHeader();
989
990 // Because there can be an umbrella header per header type,
991 // find the first non umbrella header to swap position with.
992 auto BeginPos = find_if(Range&: Ctx.InputHeaders, P: [](const HeaderFile &H) {
993 return !H.isUmbrellaHeader();
994 });
995 if (BeginPos != Ctx.InputHeaders.end() && BeginPos < It)
996 std::swap(a&: *BeginPos, b&: *It);
997 return true;
998 };
999
1000 auto FindUmbrellaHeader = [&](StringRef HeaderPath, HeaderType Type) -> bool {
1001 assert(Type != HeaderType::Unknown && "Missing header type.");
1002 if (!HeaderPath.empty()) {
1003 auto EscapedString = Regex::escape(String: HeaderPath);
1004 Regex UmbrellaRegex(EscapedString);
1005 if (!MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type)) {
1006 Diags->Report(DiagID: diag::err_no_such_umbrella_header_file)
1007 << HeaderPath << (unsigned)Type;
1008 return false;
1009 }
1010 } else if (!FrameworkName.empty() && (Type != HeaderType::Project)) {
1011 auto UmbrellaName = "/" + Regex::escape(String: FrameworkName);
1012 if (Type == HeaderType::Public)
1013 UmbrellaName += "\\.h";
1014 else
1015 UmbrellaName += "[_]?Private\\.h";
1016 Regex UmbrellaRegex(UmbrellaName);
1017 MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type);
1018 }
1019 return true;
1020 };
1021 if (!FindUmbrellaHeader(DriverOpts.PublicUmbrellaHeader,
1022 HeaderType::Public) ||
1023 !FindUmbrellaHeader(DriverOpts.PrivateUmbrellaHeader,
1024 HeaderType::Private) ||
1025 !FindUmbrellaHeader(DriverOpts.ProjectUmbrellaHeader,
1026 HeaderType::Project))
1027 return Ctx;
1028
1029 // Parse binary dylib and initialize verifier.
1030 if (DriverOpts.DylibToVerify.empty()) {
1031 Ctx.Verifier = std::make_unique<DylibVerifier>();
1032 return Ctx;
1033 }
1034
1035 auto Buffer = FM->getBufferForFile(Filename: DriverOpts.DylibToVerify);
1036 if (auto Err = Buffer.getError()) {
1037 Diags->Report(DiagID: diag::err_cannot_open_file)
1038 << DriverOpts.DylibToVerify << Err.message();
1039 return Ctx;
1040 }
1041
1042 DylibReader::ParseOption PO;
1043 PO.Undefineds = false;
1044 Expected<Records> Slices =
1045 DylibReader::readFile(Buffer: (*Buffer)->getMemBufferRef(), Opt: PO);
1046 if (auto Err = Slices.takeError()) {
1047 Diags->Report(DiagID: diag::err_cannot_open_file)
1048 << DriverOpts.DylibToVerify << std::move(Err);
1049 return Ctx;
1050 }
1051
1052 Ctx.Verifier = std::make_unique<DylibVerifier>(
1053 args: std::move(*Slices), args: std::move(ReexportedIFs), args: std::move(Aliases), args&: Diags,
1054 args&: DriverOpts.VerifyMode, args&: DriverOpts.Zippered, args&: DriverOpts.Demangle,
1055 args&: DriverOpts.DSYMPath);
1056 return Ctx;
1057}
1058
1059void Options::addConditionalCC1Args(std::vector<std::string> &ArgStrings,
1060 const llvm::Triple &Targ,
1061 const HeaderType Type) {
1062 // Unique to architecture (Xarch) options hold no arguments to pass along for
1063 // frontend.
1064
1065 // Add specific to platform arguments.
1066 PathSeq PlatformSearchPaths =
1067 getPathsForPlatform(Paths: FEOpts.SystemFwkPaths, Platform: mapToPlatformType(Target: Targ));
1068 for (StringRef Path : PlatformSearchPaths) {
1069 ArgStrings.push_back(x: "-iframework");
1070 ArgStrings.push_back(x: Path.str());
1071 }
1072
1073 // Add specific to header type arguments.
1074 if (Type == HeaderType::Project)
1075 for (const StringRef A : ProjectLevelArgs)
1076 ArgStrings.emplace_back(args: A);
1077}
1078
1079} // namespace installapi
1080} // namespace clang
1081