xref: /llvm-project/clang/tools/clang-installapi/ClangInstallAPI.cpp (revision df9a14d7bbf1180e4f1474254c9d7ed6bcb4ce55)
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, 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(InputFile, /*ModTime=*/0, 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(InitialArgs, std::back_inserter(Args));
63   Args.push_back(InputFile);
64 
65   // Create & run invocation.
66   clang::tooling::ToolInvocation Invocation(
67       std::move(Args), std::make_unique<InstallAPIAction>(Ctx), Ctx.FM);
68   return Invocation.run();
69 }
70 
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);
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(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(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))
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(FM->getVirtualFileSystem());
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>(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);
131       Ctx.Type = Type;
132       StringRef HeaderLabel = getName(Ctx.Type);
133       if (!runFrontend(ProgName, HeaderLabel, Opts.DriverOpts.Verbose, Ctx,
134                        InMemoryFileSystem.get(), 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(FinalArguments, ExtraArgs);
141         if (!runFrontend(ProgName, Label + " " + HeaderLabel,
142                          Opts.DriverOpts.Verbose, Ctx, InMemoryFileSystem.get(),
143                          FinalArguments))
144           return EXIT_FAILURE;
145       }
146     }
147     FrontendRecords.emplace_back(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(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(TargetInfo.first);
166     IF.setFromBinaryAttrs(Ctx.BA, 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(*Out, IF, Ctx.FT)) {
185     Diag->Report(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 
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;
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(llvm::ArrayRef(argv, argc), ProgName);
208 }
209