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