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::driver::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::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 return Invocation.run();
69}
70
71static bool run(ArrayRef<const char *> Args, const char *ProgName) {
72 // Setup Diagnostics engine.
73 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
74 const llvm::opt::OptTable &ClangOpts = clang::driver::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 IntrusiveRefCntPtr<DiagnosticsEngine> Diag = new clang::DiagnosticsEngine(
81 new clang::DiagnosticIDs(), DiagOpts.get(),
82 new clang::TextDiagnosticPrinter(llvm::errs(), DiagOpts.get()));
83
84 // Create file manager for all file operations and holding in-memory generated
85 // inputs.
86 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
87 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
88 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
89 new llvm::vfs::InMemoryFileSystem);
90 OverlayFileSystem->pushOverlay(FS: InMemoryFileSystem);
91 IntrusiveRefCntPtr<clang::FileManager> FM(
92 new FileManager(clang::FileSystemOptions(), OverlayFileSystem));
93
94 // Capture all options and diagnose any errors.
95 Options Opts(*Diag, FM.get(), Args, ProgName);
96 if (Diag->hasErrorOccurred())
97 return EXIT_FAILURE;
98
99 InstallAPIContext Ctx = Opts.createContext();
100 if (Diag->hasErrorOccurred())
101 return EXIT_FAILURE;
102
103 if (!Opts.DriverOpts.DylibToVerify.empty()) {
104 TargetList Targets;
105 llvm::for_each(Range&: Opts.DriverOpts.Targets,
106 F: [&](const auto &T) { Targets.push_back(Elt: T.first); });
107 if (!Ctx.Verifier->verifyBinaryAttrs(ProvidedTargets: Targets, ProvidedBA: Ctx.BA, ProvidedReexports: Ctx.Reexports,
108 ProvidedClients: Opts.LinkerOpts.AllowableClients,
109 ProvidedRPaths: Opts.LinkerOpts.RPaths, FT: Ctx.FT))
110 return EXIT_FAILURE;
111 };
112
113 // Set up compilation.
114 std::unique_ptr<CompilerInstance> CI(new CompilerInstance());
115 CI->setFileManager(FM.get());
116 CI->createDiagnostics();
117 if (!CI->hasDiagnostics())
118 return EXIT_FAILURE;
119
120 // Execute, verify and gather AST results.
121 // An invocation is ran for each unique target triple and for each header
122 // access level.
123 Records FrontendRecords;
124 for (const auto &[Targ, Trip] : Opts.DriverOpts.Targets) {
125 Ctx.Verifier->setTarget(Targ);
126 Ctx.Slice = std::make_shared<FrontendRecordsSlice>(args: Trip);
127 for (const HeaderType Type :
128 {HeaderType::Public, HeaderType::Private, HeaderType::Project}) {
129 std::vector<std::string> ArgStrings = Opts.getClangFrontendArgs();
130 Opts.addConditionalCC1Args(ArgStrings, Targ: Trip, Type);
131 Ctx.Type = Type;
132 StringRef HeaderLabel = getName(T: Ctx.Type);
133 if (!runFrontend(ProgName, Label: HeaderLabel, Verbose: Opts.DriverOpts.Verbose, Ctx,
134 FS: InMemoryFileSystem.get(), InitialArgs: ArgStrings))
135 return EXIT_FAILURE;
136
137 // Run extra passes for unique compiler arguments.
138 for (const auto &[Label, ExtraArgs] : Opts.FEOpts.UniqueArgs) {
139 std::vector<std::string> FinalArguments = ArgStrings;
140 llvm::append_range(C&: FinalArguments, R: ExtraArgs);
141 if (!runFrontend(ProgName, Label: Label + " " + HeaderLabel,
142 Verbose: Opts.DriverOpts.Verbose, Ctx, FS: InMemoryFileSystem.get(),
143 InitialArgs: FinalArguments))
144 return EXIT_FAILURE;
145 }
146 }
147 FrontendRecords.emplace_back(Args: std::move(Ctx.Slice));
148 }
149
150 if (Ctx.Verifier->verifyRemainingSymbols() == DylibVerifier::Result::Invalid)
151 return EXIT_FAILURE;
152
153 // After symbols have been collected, prepare to write output.
154 auto Out = CI->createOutputFile(OutputPath: Ctx.OutputLoc, /*Binary=*/false,
155 /*RemoveFileOnSignal=*/false,
156 /*UseTemporary=*/false,
157 /*CreateMissingDirectories=*/false);
158 if (!Out)
159 return EXIT_FAILURE;
160
161 // Assign attributes for serialization.
162 InterfaceFile IF(Ctx.Verifier->takeExports());
163 // Assign attributes that are the same per slice first.
164 for (const auto &TargetInfo : Opts.DriverOpts.Targets) {
165 IF.addTarget(Target: TargetInfo.first);
166 IF.setFromBinaryAttrs(BA: Ctx.BA, Targ: TargetInfo.first);
167 }
168 // Then assign potentially different attributes per slice after.
169 auto assignLibAttrs =
170 [&IF](
171 const auto &Attrs,
172 std::function<void(InterfaceFile *, StringRef, const Target &)> Add) {
173 for (const auto &Lib : Attrs)
174 for (const auto &T : IF.targets(Lib.getValue()))
175 Add(&IF, Lib.getKey(), T);
176 };
177
178 assignLibAttrs(Opts.LinkerOpts.AllowableClients,
179 &InterfaceFile::addAllowableClient);
180 assignLibAttrs(Opts.LinkerOpts.RPaths, &InterfaceFile::addRPath);
181 assignLibAttrs(Ctx.Reexports, &InterfaceFile::addReexportedLibrary);
182
183 // Write output file and perform CI cleanup.
184 if (auto Err = TextAPIWriter::writeToStream(OS&: *Out, File: IF, FileKind: Ctx.FT)) {
185 Diag->Report(DiagID: diag::err_cannot_write_file)
186 << Ctx.OutputLoc << std::move(Err);
187 CI->clearOutputFiles(/*EraseFiles=*/true);
188 return EXIT_FAILURE;
189 }
190
191 CI->clearOutputFiles(/*EraseFiles=*/false);
192 return EXIT_SUCCESS;
193}
194
195int clang_installapi_main(int argc, char **argv,
196 const llvm::ToolContext &ToolContext) {
197 // Standard set up, so program fails gracefully.
198 llvm::sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]);
199 llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
200 llvm::llvm_shutdown_obj Shutdown;
201
202 if (llvm::sys::Process::FixupStandardFileDescriptors())
203 return EXIT_FAILURE;
204
205 const char *ProgName =
206 ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path;
207 return run(Args: llvm::ArrayRef(argv, argc), ProgName);
208}
209