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, 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
72static 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
183int 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

source code of clang/tools/clang-installapi/ClangInstallAPI.cpp