1 //===-- ClangInstallAPI.cpp ----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This is the entry point to clang-installapi; it is a wrapper
10 // for functionality in the InstallAPI clang library.
12 //===----------------------------------------------------------------------===//
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"
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
;
42 static bool runFrontend(StringRef ProgName
, Twine Label
, bool Verbose
,
43 InstallAPIContext
&Ctx
,
44 llvm::vfs::InMemoryFileSystem
*FS
,
45 const ArrayRef
<std::string
> InitialArgs
) {
47 std::unique_ptr
<llvm::MemoryBuffer
> ProcessedInput
= createInputBuffer(Ctx
);
48 // Skip invoking cc1 when there are no header inputs.
53 llvm::errs() << Label
<< " Headers:\n"
54 << ProcessedInput
->getBuffer() << "\n\n";
56 std::string InputFile
= ProcessedInput
->getBufferIdentifier().str();
57 FS
->addFile(InputFile
, /*ModTime=*/0, std::move(ProcessedInput
));
58 // Reconstruct arguments with unique values like target triple or input
60 std::vector
<std::string
> Args
= {ProgName
.data(), "-target",
61 Ctx
.Slice
->getTriple().str().c_str()};
62 llvm::copy(InitialArgs
, std::back_inserter(Args
));
63 Args
.push_back(InputFile
);
65 // Create & run invocation.
66 clang::tooling::ToolInvocation
Invocation(
67 std::move(Args
), std::make_unique
<InstallAPIAction
>(Ctx
), Ctx
.FM
);
68 return Invocation
.run();
71 static 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 ArrayRef(Args
).slice(1), MissingArgIndex
, MissingArgCount
);
78 ParseDiagnosticArgs(*DiagOpts
, ParsedArgs
);
80 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diag
= new clang::DiagnosticsEngine(
81 new clang::DiagnosticIDs(), DiagOpts
.get(),
82 new clang::TextDiagnosticPrinter(llvm::errs(), DiagOpts
.get()));
84 // Create file manager for all file operations and holding in-memory generated
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(InMemoryFileSystem
);
91 IntrusiveRefCntPtr
<clang::FileManager
> FM(
92 new FileManager(clang::FileSystemOptions(), OverlayFileSystem
));
94 // Capture all options and diagnose any errors.
95 Options
Opts(*Diag
, FM
.get(), Args
, ProgName
);
96 if (Diag
->hasErrorOccurred())
99 InstallAPIContext Ctx
= Opts
.createContext();
100 if (Diag
->hasErrorOccurred())
103 if (!Opts
.DriverOpts
.DylibToVerify
.empty()) {
105 llvm::for_each(Opts
.DriverOpts
.Targets
,
106 [&](const auto &T
) { Targets
.push_back(T
.first
); });
107 if (!Ctx
.Verifier
->verifyBinaryAttrs(Targets
, Ctx
.BA
, Ctx
.Reexports
,
108 Opts
.LinkerOpts
.AllowableClients
,
109 Opts
.LinkerOpts
.RPaths
, Ctx
.FT
))
113 // Set up compilation.
114 std::unique_ptr
<CompilerInstance
> CI(new CompilerInstance());
115 CI
->setFileManager(FM
.get());
116 CI
->createDiagnostics(FM
->getVirtualFileSystem());
117 if (!CI
->hasDiagnostics())
120 // Execute, verify and gather AST results.
121 // An invocation is ran for each unique target triple and for each header
123 Records FrontendRecords
;
124 for (const auto &[Targ
, Trip
] : Opts
.DriverOpts
.Targets
) {
125 Ctx
.Verifier
->setTarget(Targ
);
126 Ctx
.Slice
= std::make_shared
<FrontendRecordsSlice
>(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
, Trip
, Type
);
132 StringRef HeaderLabel
= getName(Ctx
.Type
);
133 if (!runFrontend(ProgName
, HeaderLabel
, Opts
.DriverOpts
.Verbose
, Ctx
,
134 InMemoryFileSystem
.get(), ArgStrings
))
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(FinalArguments
, ExtraArgs
);
141 if (!runFrontend(ProgName
, Label
+ " " + HeaderLabel
,
142 Opts
.DriverOpts
.Verbose
, Ctx
, InMemoryFileSystem
.get(),
147 FrontendRecords
.emplace_back(std::move(Ctx
.Slice
));
150 if (Ctx
.Verifier
->verifyRemainingSymbols() == DylibVerifier::Result::Invalid
)
153 // After symbols have been collected, prepare to write output.
154 auto Out
= CI
->createOutputFile(Ctx
.OutputLoc
, /*Binary=*/false,
155 /*RemoveFileOnSignal=*/false,
156 /*UseTemporary=*/false,
157 /*CreateMissingDirectories=*/false);
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(TargetInfo
.first
);
166 IF
.setFromBinaryAttrs(Ctx
.BA
, TargetInfo
.first
);
168 // Then assign potentially different attributes per slice after.
169 auto assignLibAttrs
=
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
);
178 assignLibAttrs(Opts
.LinkerOpts
.AllowableClients
,
179 &InterfaceFile::addAllowableClient
);
180 assignLibAttrs(Opts
.LinkerOpts
.RPaths
, &InterfaceFile::addRPath
);
181 assignLibAttrs(Ctx
.Reexports
, &InterfaceFile::addReexportedLibrary
);
183 // Write output file and perform CI cleanup.
184 if (auto Err
= TextAPIWriter::writeToStream(*Out
, IF
, Ctx
.FT
)) {
185 Diag
->Report(diag::err_cannot_write_file
)
186 << Ctx
.OutputLoc
<< std::move(Err
);
187 CI
->clearOutputFiles(/*EraseFiles=*/true);
191 CI
->clearOutputFiles(/*EraseFiles=*/false);
195 int clang_installapi_main(int argc
, char **argv
,
196 const llvm::ToolContext
&ToolContext
) {
197 // Standard set up, so program fails gracefully.
198 llvm::sys::PrintStackTraceOnErrorSignal(argv
[0]);
199 llvm::PrettyStackTraceProgram
StackPrinter(argc
, argv
);
200 llvm::llvm_shutdown_obj Shutdown
;
202 if (llvm::sys::Process::FixupStandardFileDescriptors())
205 const char *ProgName
=
206 ToolContext
.NeedsPrependArg
? ToolContext
.PrependArg
: ToolContext
.Path
;
207 return run(llvm::ArrayRef(argv
, argc
), ProgName
);