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
36using namespace clang;
37using namespace clang::installapi;
38using namespace clang::options;
39using namespace llvm::opt;
40using namespace llvm::MachO;
41
42static bool runFrontend(StringRef ProgName, Twine Label, 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() << Label << " 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::append_range(C&: Args, R: InitialArgs);
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 return Invocation.run();
69}
70
71static bool run(ArrayRef<const char *> Args, const char *ProgName) {
72 // Setup Diagnostics engine.
73 DiagnosticOptions DiagOpts;
74 const llvm::opt::OptTable &ClangOpts = getDriverOptTable();
75 unsigned MissingArgIndex, MissingArgCount;
76 llvm::opt::InputArgList ParsedArgs = ClangOpts.ParseArgs(
77 Args: ArrayRef(Args).slice(N: 1), MissingArgIndex, MissingArgCount);
78 ParseDiagnosticArgs(Opts&: DiagOpts, Args&: ParsedArgs);
79
80 auto Diag = llvm::makeIntrusiveRefCnt<clang::DiagnosticsEngine>(
81 A: clang::DiagnosticIDs::create(), A&: DiagOpts,
82 A: new clang::TextDiagnosticPrinter(llvm::errs(), DiagOpts));
83
84 // Create file manager for all file operations and holding in-memory generated
85 // inputs.
86 auto OverlayFileSystem =
87 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
88 A: llvm::vfs::getRealFileSystem());
89 auto InMemoryFileSystem =
90 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
91 OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem);
92 IntrusiveRefCntPtr<clang::FileManager> FM =
93 llvm::makeIntrusiveRefCnt<FileManager>(A: clang::FileSystemOptions(),
94 A&: OverlayFileSystem);
95
96 // Capture all options and diagnose any errors.
97 Options Opts(*Diag, FM.get(), Args, ProgName);
98 if (Diag->hasErrorOccurred())
99 return EXIT_FAILURE;
100
101 InstallAPIContext Ctx = Opts.createContext();
102 if (Diag->hasErrorOccurred())
103 return EXIT_FAILURE;
104
105 if (!Opts.DriverOpts.DylibToVerify.empty()) {
106 TargetList Targets;
107 for (const auto &T : Opts.DriverOpts.Targets)
108 Targets.push_back(Elt: T.first);
109 if (!Ctx.Verifier->verifyBinaryAttrs(ProvidedTargets: Targets, ProvidedBA: Ctx.BA, ProvidedReexports: Ctx.Reexports,
110 ProvidedClients: Opts.LinkerOpts.AllowableClients,
111 ProvidedRPaths: Opts.LinkerOpts.RPaths, FT: Ctx.FT))
112 return EXIT_FAILURE;
113 };
114
115 // Set up compilation.
116 std::unique_ptr<CompilerInstance> CI(new CompilerInstance());
117 CI->setVirtualFileSystem(FM->getVirtualFileSystemPtr());
118 CI->setFileManager(FM);
119 CI->createDiagnostics();
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 std::vector<std::string> ArgStrings = Opts.getClangFrontendArgs();
131 Opts.addConditionalCC1Args(ArgStrings, Targ: Trip, Type);
132 Ctx.Type = Type;
133 StringRef HeaderLabel = getName(T: Ctx.Type);
134 if (!runFrontend(ProgName, Label: HeaderLabel, Verbose: Opts.DriverOpts.Verbose, Ctx,
135 FS: InMemoryFileSystem.get(), InitialArgs: ArgStrings))
136 return EXIT_FAILURE;
137
138 // Run extra passes for unique compiler arguments.
139 for (const auto &[Label, ExtraArgs] : Opts.FEOpts.UniqueArgs) {
140 std::vector<std::string> FinalArguments = ArgStrings;
141 llvm::append_range(C&: FinalArguments, R: ExtraArgs);
142 if (!runFrontend(ProgName, Label: Label + " " + HeaderLabel,
143 Verbose: Opts.DriverOpts.Verbose, Ctx, FS: InMemoryFileSystem.get(),
144 InitialArgs: FinalArguments))
145 return EXIT_FAILURE;
146 }
147 }
148 FrontendRecords.emplace_back(Args: std::move(Ctx.Slice));
149 }
150
151 if (Ctx.Verifier->verifyRemainingSymbols() == DylibVerifier::Result::Invalid)
152 return EXIT_FAILURE;
153
154 // After symbols have been collected, prepare to write output.
155 auto Out = CI->getOrCreateOutputManager().createFile(
156 Path: Ctx.OutputLoc, Config: llvm::vfs::OutputConfig()
157 .setTextWithCRLF()
158 .setNoImplyCreateDirectories()
159 .setNoAtomicWrite());
160 if (!Out) {
161 Diag->Report(DiagID: diag::err_cannot_open_file) << Ctx.OutputLoc;
162 return EXIT_FAILURE;
163 }
164
165 // Assign attributes for serialization.
166 InterfaceFile IF(Ctx.Verifier->takeExports());
167 // Assign attributes that are the same per slice first.
168 for (const auto &TargetInfo : Opts.DriverOpts.Targets) {
169 IF.addTarget(Target: TargetInfo.first);
170 IF.setFromBinaryAttrs(BA: Ctx.BA, Targ: TargetInfo.first);
171 }
172 // Then assign potentially different attributes per slice after.
173 auto assignLibAttrs =
174 [&IF](
175 const auto &Attrs,
176 std::function<void(InterfaceFile *, StringRef, const Target &)> Add) {
177 for (const auto &[Attr, ArchSet] : Attrs.get())
178 for (const auto &T : IF.targets(ArchSet))
179 Add(&IF, Attr, T);
180 };
181
182 assignLibAttrs(Opts.LinkerOpts.AllowableClients,
183 &InterfaceFile::addAllowableClient);
184 assignLibAttrs(Opts.LinkerOpts.RPaths, &InterfaceFile::addRPath);
185 assignLibAttrs(Ctx.Reexports, &InterfaceFile::addReexportedLibrary);
186
187 // Write output file and perform CI cleanup.
188 if (auto Err = TextAPIWriter::writeToStream(OS&: *Out, File: IF, FileKind: Ctx.FT)) {
189 Diag->Report(DiagID: diag::err_cannot_write_file)
190 << Ctx.OutputLoc << std::move(Err);
191 if (auto Err = Out->discard())
192 llvm::consumeError(Err: std::move(Err));
193 return EXIT_FAILURE;
194 }
195 if (auto Err = Out->keep()) {
196 Diag->Report(DiagID: diag::err_cannot_write_file)
197 << Ctx.OutputLoc << std::move(Err);
198 return EXIT_FAILURE;
199 }
200 return EXIT_SUCCESS;
201}
202
203int clang_installapi_main(int argc, char **argv,
204 const llvm::ToolContext &ToolContext) {
205 // Standard set up, so program fails gracefully.
206 llvm::sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]);
207 llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
208 llvm::llvm_shutdown_obj Shutdown;
209
210 if (llvm::sys::Process::FixupStandardFileDescriptors())
211 return EXIT_FAILURE;
212
213 const char *ProgName =
214 ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path;
215 return run(Args: llvm::ArrayRef(argv, argc), ProgName);
216}
217