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