1 | //===-- ClangInstallAPI.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 | // This is the entry point to clang-installapi; it is a wrapper |
10 | // for functionality in the InstallAPI clang library. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "Options.h" |
15 | #include "clang/Basic/Diagnostic.h" |
16 | #include "clang/Basic/DiagnosticFrontend.h" |
17 | #include "clang/Driver/DriverDiagnostic.h" |
18 | #include "clang/Driver/Tool.h" |
19 | #include "clang/Frontend/TextDiagnosticPrinter.h" |
20 | #include "clang/InstallAPI/Frontend.h" |
21 | #include "clang/InstallAPI/FrontendRecords.h" |
22 | #include "clang/InstallAPI/InstallAPIDiagnostic.h" |
23 | #include "clang/InstallAPI/MachO.h" |
24 | #include "clang/Tooling/Tooling.h" |
25 | #include "llvm/ADT/ArrayRef.h" |
26 | #include "llvm/Option/Option.h" |
27 | #include "llvm/Support/CommandLine.h" |
28 | #include "llvm/Support/LLVMDriver.h" |
29 | #include "llvm/Support/ManagedStatic.h" |
30 | #include "llvm/Support/PrettyStackTrace.h" |
31 | #include "llvm/Support/Process.h" |
32 | #include "llvm/Support/Signals.h" |
33 | #include "llvm/TargetParser/Host.h" |
34 | #include <memory> |
35 | |
36 | using namespace clang; |
37 | using namespace clang::installapi; |
38 | using namespace clang::driver::options; |
39 | using namespace llvm::opt; |
40 | using namespace llvm::MachO; |
41 | |
42 | static bool runFrontend(StringRef ProgName, bool Verbose, |
43 | InstallAPIContext &Ctx, |
44 | llvm::vfs::InMemoryFileSystem *FS, |
45 | const ArrayRef<std::string> InitialArgs) { |
46 | |
47 | std::unique_ptr<llvm::MemoryBuffer> ProcessedInput = createInputBuffer(Ctx); |
48 | // Skip invoking cc1 when there are no header inputs. |
49 | if (!ProcessedInput) |
50 | return true; |
51 | |
52 | if (Verbose) |
53 | llvm::errs() << getName(T: Ctx.Type) << " Headers:\n" |
54 | << ProcessedInput->getBuffer() << "\n\n" ; |
55 | |
56 | std::string InputFile = ProcessedInput->getBufferIdentifier().str(); |
57 | FS->addFile(Path: InputFile, /*ModTime=*/ModificationTime: 0, Buffer: std::move(ProcessedInput)); |
58 | // Reconstruct arguments with unique values like target triple or input |
59 | // headers. |
60 | std::vector<std::string> Args = {ProgName.data(), "-target" , |
61 | Ctx.Slice->getTriple().str().c_str()}; |
62 | llvm::copy(Range: InitialArgs, Out: std::back_inserter(x&: Args)); |
63 | Args.push_back(x: InputFile); |
64 | |
65 | // Create & run invocation. |
66 | clang::tooling::ToolInvocation Invocation( |
67 | std::move(Args), std::make_unique<InstallAPIAction>(args&: Ctx), Ctx.FM); |
68 | |
69 | return Invocation.run(); |
70 | } |
71 | |
72 | static bool run(ArrayRef<const char *> Args, const char *ProgName) { |
73 | // Setup Diagnostics engine. |
74 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); |
75 | const llvm::opt::OptTable &ClangOpts = clang::driver::getDriverOptTable(); |
76 | unsigned MissingArgIndex, MissingArgCount; |
77 | llvm::opt::InputArgList ParsedArgs = ClangOpts.ParseArgs( |
78 | Args: ArrayRef(Args).slice(N: 1), MissingArgIndex, MissingArgCount); |
79 | ParseDiagnosticArgs(Opts&: *DiagOpts, Args&: ParsedArgs); |
80 | |
81 | IntrusiveRefCntPtr<DiagnosticsEngine> Diag = new clang::DiagnosticsEngine( |
82 | new clang::DiagnosticIDs(), DiagOpts.get(), |
83 | new clang::TextDiagnosticPrinter(llvm::errs(), DiagOpts.get())); |
84 | |
85 | // Create file manager for all file operations and holding in-memory generated |
86 | // inputs. |
87 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( |
88 | new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); |
89 | llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
90 | new llvm::vfs::InMemoryFileSystem); |
91 | OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem); |
92 | IntrusiveRefCntPtr<clang::FileManager> FM( |
93 | new FileManager(clang::FileSystemOptions(), OverlayFileSystem)); |
94 | |
95 | // Capture all options and diagnose any errors. |
96 | Options Opts(*Diag, FM.get(), Args, ProgName); |
97 | if (Diag->hasErrorOccurred()) |
98 | return EXIT_FAILURE; |
99 | |
100 | InstallAPIContext Ctx = Opts.createContext(); |
101 | if (Diag->hasErrorOccurred()) |
102 | return EXIT_FAILURE; |
103 | |
104 | if (!Opts.DriverOpts.DylibToVerify.empty()) { |
105 | TargetList Targets; |
106 | llvm::for_each(Range&: Opts.DriverOpts.Targets, |
107 | F: [&](const auto &T) { Targets.push_back(Elt: T.first); }); |
108 | if (!Ctx.Verifier->verifyBinaryAttrs(ProvidedTargets: Targets, ProvidedBA: Ctx.BA, ProvidedReexports: Ctx.Reexports, |
109 | ProvidedClients: Opts.LinkerOpts.AllowableClients, |
110 | ProvidedRPaths: Opts.LinkerOpts.RPaths, FT: Ctx.FT)) |
111 | return EXIT_FAILURE; |
112 | }; |
113 | |
114 | // Set up compilation. |
115 | std::unique_ptr<CompilerInstance> CI(new CompilerInstance()); |
116 | CI->setFileManager(FM.get()); |
117 | CI->createDiagnostics(); |
118 | if (!CI->hasDiagnostics()) |
119 | return EXIT_FAILURE; |
120 | |
121 | // Execute, verify and gather AST results. |
122 | // An invocation is ran for each unique target triple and for each header |
123 | // access level. |
124 | Records FrontendRecords; |
125 | for (const auto &[Targ, Trip] : Opts.DriverOpts.Targets) { |
126 | Ctx.Verifier->setTarget(Targ); |
127 | Ctx.Slice = std::make_shared<FrontendRecordsSlice>(args: Trip); |
128 | for (const HeaderType Type : |
129 | {HeaderType::Public, HeaderType::Private, HeaderType::Project}) { |
130 | Ctx.Type = Type; |
131 | if (!runFrontend(ProgName, Verbose: Opts.DriverOpts.Verbose, Ctx, |
132 | FS: InMemoryFileSystem.get(), InitialArgs: Opts.getClangFrontendArgs())) |
133 | return EXIT_FAILURE; |
134 | } |
135 | FrontendRecords.emplace_back(Args: std::move(Ctx.Slice)); |
136 | } |
137 | |
138 | if (Ctx.Verifier->verifyRemainingSymbols() == DylibVerifier::Result::Invalid) |
139 | return EXIT_FAILURE; |
140 | |
141 | // After symbols have been collected, prepare to write output. |
142 | auto Out = CI->createOutputFile(OutputPath: Ctx.OutputLoc, /*Binary=*/false, |
143 | /*RemoveFileOnSignal=*/false, |
144 | /*UseTemporary=*/false, |
145 | /*CreateMissingDirectories=*/false); |
146 | if (!Out) |
147 | return EXIT_FAILURE; |
148 | |
149 | // Assign attributes for serialization. |
150 | InterfaceFile IF(Ctx.Verifier->takeExports()); |
151 | // Assign attributes that are the same per slice first. |
152 | for (const auto &TargetInfo : Opts.DriverOpts.Targets) { |
153 | IF.addTarget(Target: TargetInfo.first); |
154 | IF.setFromBinaryAttrs(BA: Ctx.BA, Targ: TargetInfo.first); |
155 | } |
156 | // Then assign potentially different attributes per slice after. |
157 | auto assignLibAttrs = |
158 | [&IF]( |
159 | const auto &Attrs, |
160 | std::function<void(InterfaceFile *, StringRef, const Target &)> Add) { |
161 | for (const auto &Lib : Attrs) |
162 | for (const auto &T : IF.targets(Lib.getValue())) |
163 | Add(&IF, Lib.getKey(), T); |
164 | }; |
165 | |
166 | assignLibAttrs(Opts.LinkerOpts.AllowableClients, |
167 | &InterfaceFile::addAllowableClient); |
168 | assignLibAttrs(Opts.LinkerOpts.RPaths, &InterfaceFile::addRPath); |
169 | assignLibAttrs(Ctx.Reexports, &InterfaceFile::addReexportedLibrary); |
170 | |
171 | // Write output file and perform CI cleanup. |
172 | if (auto Err = TextAPIWriter::writeToStream(OS&: *Out, File: IF, FileKind: Ctx.FT)) { |
173 | Diag->Report(diag::err_cannot_write_file) |
174 | << Ctx.OutputLoc << std::move(Err); |
175 | CI->clearOutputFiles(/*EraseFiles=*/true); |
176 | return EXIT_FAILURE; |
177 | } |
178 | |
179 | CI->clearOutputFiles(/*EraseFiles=*/false); |
180 | return EXIT_SUCCESS; |
181 | } |
182 | |
183 | int clang_installapi_main(int argc, char **argv, |
184 | const llvm::ToolContext &ToolContext) { |
185 | // Standard set up, so program fails gracefully. |
186 | llvm::sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]); |
187 | llvm::PrettyStackTraceProgram StackPrinter(argc, argv); |
188 | llvm::llvm_shutdown_obj Shutdown; |
189 | |
190 | if (llvm::sys::Process::FixupStandardFileDescriptors()) |
191 | return EXIT_FAILURE; |
192 | |
193 | const char *ProgName = |
194 | ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path; |
195 | return run(Args: llvm::ArrayRef(argv, argc), ProgName); |
196 | } |
197 | |