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