xref: /llvm-project/clang/tools/clang-installapi/ClangInstallAPI.cpp (revision df9a14d7bbf1180e4f1474254c9d7ed6bcb4ce55)
10a518db9SCyndy Ishida //===-- ClangInstallAPI.cpp ----------------------------------------------===//
20a518db9SCyndy Ishida //
30a518db9SCyndy Ishida // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40a518db9SCyndy Ishida // See https://llvm.org/LICENSE.txt for license information.
50a518db9SCyndy Ishida // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60a518db9SCyndy Ishida //
70a518db9SCyndy Ishida //===----------------------------------------------------------------------===//
80a518db9SCyndy Ishida //
90a518db9SCyndy Ishida // This is the entry point to clang-installapi; it is a wrapper
100a518db9SCyndy Ishida // for functionality in the InstallAPI clang library.
110a518db9SCyndy Ishida //
120a518db9SCyndy Ishida //===----------------------------------------------------------------------===//
130a518db9SCyndy Ishida 
140a518db9SCyndy Ishida #include "Options.h"
15c6cbf81cSCyndy Ishida #include "clang/Basic/Diagnostic.h"
16c6cbf81cSCyndy Ishida #include "clang/Basic/DiagnosticFrontend.h"
170a518db9SCyndy Ishida #include "clang/Driver/DriverDiagnostic.h"
18c6cbf81cSCyndy Ishida #include "clang/Driver/Tool.h"
190a518db9SCyndy Ishida #include "clang/Frontend/TextDiagnosticPrinter.h"
20c6cbf81cSCyndy Ishida #include "clang/InstallAPI/Frontend.h"
21a38b7a43SCyndy Ishida #include "clang/InstallAPI/FrontendRecords.h"
22c51095f5SCyndy Ishida #include "clang/InstallAPI/InstallAPIDiagnostic.h"
23a38b7a43SCyndy Ishida #include "clang/InstallAPI/MachO.h"
24c6cbf81cSCyndy Ishida #include "clang/Tooling/Tooling.h"
250a518db9SCyndy Ishida #include "llvm/ADT/ArrayRef.h"
260a518db9SCyndy Ishida #include "llvm/Option/Option.h"
270a518db9SCyndy Ishida #include "llvm/Support/CommandLine.h"
280a518db9SCyndy Ishida #include "llvm/Support/LLVMDriver.h"
290a518db9SCyndy Ishida #include "llvm/Support/ManagedStatic.h"
300a518db9SCyndy Ishida #include "llvm/Support/PrettyStackTrace.h"
310a518db9SCyndy Ishida #include "llvm/Support/Process.h"
320a518db9SCyndy Ishida #include "llvm/Support/Signals.h"
330a518db9SCyndy Ishida #include "llvm/TargetParser/Host.h"
34c6cbf81cSCyndy Ishida #include <memory>
350a518db9SCyndy Ishida 
360a518db9SCyndy Ishida using namespace clang;
370a518db9SCyndy Ishida using namespace clang::installapi;
380a518db9SCyndy Ishida using namespace clang::driver::options;
390a518db9SCyndy Ishida using namespace llvm::opt;
400a518db9SCyndy Ishida using namespace llvm::MachO;
410a518db9SCyndy Ishida 
42062f6fe3SCyndy Ishida static bool runFrontend(StringRef ProgName, Twine Label, bool Verbose,
4317ede03aSCyndy Ishida                         InstallAPIContext &Ctx,
44c6cbf81cSCyndy Ishida                         llvm::vfs::InMemoryFileSystem *FS,
45c6cbf81cSCyndy Ishida                         const ArrayRef<std::string> InitialArgs) {
46c6cbf81cSCyndy Ishida 
47c6cbf81cSCyndy Ishida   std::unique_ptr<llvm::MemoryBuffer> ProcessedInput = createInputBuffer(Ctx);
48c6cbf81cSCyndy Ishida   // Skip invoking cc1 when there are no header inputs.
49c6cbf81cSCyndy Ishida   if (!ProcessedInput)
50c6cbf81cSCyndy Ishida     return true;
51c6cbf81cSCyndy Ishida 
52c6cbf81cSCyndy Ishida   if (Verbose)
53062f6fe3SCyndy Ishida     llvm::errs() << Label << " Headers:\n"
54c6cbf81cSCyndy Ishida                  << ProcessedInput->getBuffer() << "\n\n";
55c6cbf81cSCyndy Ishida 
56c6cbf81cSCyndy Ishida   std::string InputFile = ProcessedInput->getBufferIdentifier().str();
57c6cbf81cSCyndy Ishida   FS->addFile(InputFile, /*ModTime=*/0, std::move(ProcessedInput));
58c6cbf81cSCyndy Ishida   // Reconstruct arguments with unique values like target triple or input
59c6cbf81cSCyndy Ishida   // headers.
60c6cbf81cSCyndy Ishida   std::vector<std::string> Args = {ProgName.data(), "-target",
61c6cbf81cSCyndy Ishida                                    Ctx.Slice->getTriple().str().c_str()};
62c6cbf81cSCyndy Ishida   llvm::copy(InitialArgs, std::back_inserter(Args));
63c6cbf81cSCyndy Ishida   Args.push_back(InputFile);
64c6cbf81cSCyndy Ishida 
65c6cbf81cSCyndy Ishida   // Create & run invocation.
66c6cbf81cSCyndy Ishida   clang::tooling::ToolInvocation Invocation(
6717ede03aSCyndy Ishida       std::move(Args), std::make_unique<InstallAPIAction>(Ctx), Ctx.FM);
68c6cbf81cSCyndy Ishida   return Invocation.run();
69c6cbf81cSCyndy Ishida }
70c6cbf81cSCyndy Ishida 
710a518db9SCyndy Ishida static bool run(ArrayRef<const char *> Args, const char *ProgName) {
720a518db9SCyndy Ishida   // Setup Diagnostics engine.
730a518db9SCyndy Ishida   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
740a518db9SCyndy Ishida   const llvm::opt::OptTable &ClangOpts = clang::driver::getDriverOptTable();
750a518db9SCyndy Ishida   unsigned MissingArgIndex, MissingArgCount;
760a518db9SCyndy Ishida   llvm::opt::InputArgList ParsedArgs = ClangOpts.ParseArgs(
770a518db9SCyndy Ishida       ArrayRef(Args).slice(1), MissingArgIndex, MissingArgCount);
780a518db9SCyndy Ishida   ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
790a518db9SCyndy Ishida 
800a518db9SCyndy Ishida   IntrusiveRefCntPtr<DiagnosticsEngine> Diag = new clang::DiagnosticsEngine(
810a518db9SCyndy Ishida       new clang::DiagnosticIDs(), DiagOpts.get(),
820a518db9SCyndy Ishida       new clang::TextDiagnosticPrinter(llvm::errs(), DiagOpts.get()));
830a518db9SCyndy Ishida 
84c6cbf81cSCyndy Ishida   // Create file manager for all file operations and holding in-memory generated
85c6cbf81cSCyndy Ishida   // inputs.
86c6cbf81cSCyndy Ishida   llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
87c6cbf81cSCyndy Ishida       new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
88c6cbf81cSCyndy Ishida   llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
89c6cbf81cSCyndy Ishida       new llvm::vfs::InMemoryFileSystem);
90c6cbf81cSCyndy Ishida   OverlayFileSystem->pushOverlay(InMemoryFileSystem);
910a518db9SCyndy Ishida   IntrusiveRefCntPtr<clang::FileManager> FM(
92c6cbf81cSCyndy Ishida       new FileManager(clang::FileSystemOptions(), OverlayFileSystem));
930a518db9SCyndy Ishida 
94c51095f5SCyndy Ishida   // Capture all options and diagnose any errors.
95c51095f5SCyndy Ishida   Options Opts(*Diag, FM.get(), Args, ProgName);
960a518db9SCyndy Ishida   if (Diag->hasErrorOccurred())
970a518db9SCyndy Ishida     return EXIT_FAILURE;
98c6cbf81cSCyndy Ishida 
990a518db9SCyndy Ishida   InstallAPIContext Ctx = Opts.createContext();
100c6cbf81cSCyndy Ishida   if (Diag->hasErrorOccurred())
101c6cbf81cSCyndy Ishida     return EXIT_FAILURE;
1020a518db9SCyndy Ishida 
10327b2d7d4SCyndy Ishida   if (!Opts.DriverOpts.DylibToVerify.empty()) {
10427b2d7d4SCyndy Ishida     TargetList Targets;
10527b2d7d4SCyndy Ishida     llvm::for_each(Opts.DriverOpts.Targets,
10627b2d7d4SCyndy Ishida                    [&](const auto &T) { Targets.push_back(T.first); });
10727b2d7d4SCyndy Ishida     if (!Ctx.Verifier->verifyBinaryAttrs(Targets, Ctx.BA, Ctx.Reexports,
10827b2d7d4SCyndy Ishida                                          Opts.LinkerOpts.AllowableClients,
10927b2d7d4SCyndy Ishida                                          Opts.LinkerOpts.RPaths, Ctx.FT))
11027b2d7d4SCyndy Ishida       return EXIT_FAILURE;
11127b2d7d4SCyndy Ishida   };
11227b2d7d4SCyndy Ishida 
1130a518db9SCyndy Ishida   // Set up compilation.
1140a518db9SCyndy Ishida   std::unique_ptr<CompilerInstance> CI(new CompilerInstance());
1150a518db9SCyndy Ishida   CI->setFileManager(FM.get());
116*df9a14d7SKadir Cetinkaya   CI->createDiagnostics(FM->getVirtualFileSystem());
1170a518db9SCyndy Ishida   if (!CI->hasDiagnostics())
1180a518db9SCyndy Ishida     return EXIT_FAILURE;
1190a518db9SCyndy Ishida 
12027b2d7d4SCyndy Ishida   // Execute, verify and gather AST results.
12117ede03aSCyndy Ishida   // An invocation is ran for each unique target triple and for each header
12217ede03aSCyndy Ishida   // access level.
123f04452deSCyndy Ishida   Records FrontendRecords;
124c6cbf81cSCyndy Ishida   for (const auto &[Targ, Trip] : Opts.DriverOpts.Targets) {
125936519f2SCyndy Ishida     Ctx.Verifier->setTarget(Targ);
126936519f2SCyndy Ishida     Ctx.Slice = std::make_shared<FrontendRecordsSlice>(Trip);
127c6cbf81cSCyndy Ishida     for (const HeaderType Type :
128c6cbf81cSCyndy Ishida          {HeaderType::Public, HeaderType::Private, HeaderType::Project}) {
129062f6fe3SCyndy Ishida       std::vector<std::string> ArgStrings = Opts.getClangFrontendArgs();
130062f6fe3SCyndy Ishida       Opts.addConditionalCC1Args(ArgStrings, Trip, Type);
131c6cbf81cSCyndy Ishida       Ctx.Type = Type;
132062f6fe3SCyndy Ishida       StringRef HeaderLabel = getName(Ctx.Type);
133062f6fe3SCyndy Ishida       if (!runFrontend(ProgName, HeaderLabel, Opts.DriverOpts.Verbose, Ctx,
134062f6fe3SCyndy Ishida                        InMemoryFileSystem.get(), ArgStrings))
135c6cbf81cSCyndy Ishida         return EXIT_FAILURE;
136062f6fe3SCyndy Ishida 
137062f6fe3SCyndy Ishida       // Run extra passes for unique compiler arguments.
138062f6fe3SCyndy Ishida       for (const auto &[Label, ExtraArgs] : Opts.FEOpts.UniqueArgs) {
139062f6fe3SCyndy Ishida         std::vector<std::string> FinalArguments = ArgStrings;
140062f6fe3SCyndy Ishida         llvm::append_range(FinalArguments, ExtraArgs);
141062f6fe3SCyndy Ishida         if (!runFrontend(ProgName, Label + " " + HeaderLabel,
142062f6fe3SCyndy Ishida                          Opts.DriverOpts.Verbose, Ctx, InMemoryFileSystem.get(),
143062f6fe3SCyndy Ishida                          FinalArguments))
144062f6fe3SCyndy Ishida           return EXIT_FAILURE;
145062f6fe3SCyndy Ishida       }
146c6cbf81cSCyndy Ishida     }
147f04452deSCyndy Ishida     FrontendRecords.emplace_back(std::move(Ctx.Slice));
148c6cbf81cSCyndy Ishida   }
149c6cbf81cSCyndy Ishida 
150e470ca89SCyndy Ishida   if (Ctx.Verifier->verifyRemainingSymbols() == DylibVerifier::Result::Invalid)
151f2794cceSCyndy Ishida     return EXIT_FAILURE;
152f2794cceSCyndy Ishida 
153c6cbf81cSCyndy Ishida   // After symbols have been collected, prepare to write output.
1540a518db9SCyndy Ishida   auto Out = CI->createOutputFile(Ctx.OutputLoc, /*Binary=*/false,
1550a518db9SCyndy Ishida                                   /*RemoveFileOnSignal=*/false,
1560a518db9SCyndy Ishida                                   /*UseTemporary=*/false,
1570a518db9SCyndy Ishida                                   /*CreateMissingDirectories=*/false);
1580a518db9SCyndy Ishida   if (!Out)
1590a518db9SCyndy Ishida     return EXIT_FAILURE;
1600a518db9SCyndy Ishida 
1610a518db9SCyndy Ishida   // Assign attributes for serialization.
1624c18681aSCyndy Ishida   InterfaceFile IF(Ctx.Verifier->takeExports());
16327b2d7d4SCyndy Ishida   // Assign attributes that are the same per slice first.
1640a518db9SCyndy Ishida   for (const auto &TargetInfo : Opts.DriverOpts.Targets) {
1650a518db9SCyndy Ishida     IF.addTarget(TargetInfo.first);
1660a518db9SCyndy Ishida     IF.setFromBinaryAttrs(Ctx.BA, TargetInfo.first);
1670a518db9SCyndy Ishida   }
16827b2d7d4SCyndy Ishida   // Then assign potentially different attributes per slice after.
16927b2d7d4SCyndy Ishida   auto assignLibAttrs =
17027b2d7d4SCyndy Ishida       [&IF](
17127b2d7d4SCyndy Ishida           const auto &Attrs,
17227b2d7d4SCyndy Ishida           std::function<void(InterfaceFile *, StringRef, const Target &)> Add) {
17327b2d7d4SCyndy Ishida         for (const auto &Lib : Attrs)
17427b2d7d4SCyndy Ishida           for (const auto &T : IF.targets(Lib.getValue()))
17527b2d7d4SCyndy Ishida             Add(&IF, Lib.getKey(), T);
17627b2d7d4SCyndy Ishida       };
17727b2d7d4SCyndy Ishida 
17827b2d7d4SCyndy Ishida   assignLibAttrs(Opts.LinkerOpts.AllowableClients,
17927b2d7d4SCyndy Ishida                  &InterfaceFile::addAllowableClient);
18027b2d7d4SCyndy Ishida   assignLibAttrs(Opts.LinkerOpts.RPaths, &InterfaceFile::addRPath);
18127b2d7d4SCyndy Ishida   assignLibAttrs(Ctx.Reexports, &InterfaceFile::addReexportedLibrary);
1820a518db9SCyndy Ishida 
1830a518db9SCyndy Ishida   // Write output file and perform CI cleanup.
1840a518db9SCyndy Ishida   if (auto Err = TextAPIWriter::writeToStream(*Out, IF, Ctx.FT)) {
185c51095f5SCyndy Ishida     Diag->Report(diag::err_cannot_write_file)
186c51095f5SCyndy Ishida         << Ctx.OutputLoc << std::move(Err);
1870a518db9SCyndy Ishida     CI->clearOutputFiles(/*EraseFiles=*/true);
1880a518db9SCyndy Ishida     return EXIT_FAILURE;
1890a518db9SCyndy Ishida   }
1900a518db9SCyndy Ishida 
1910a518db9SCyndy Ishida   CI->clearOutputFiles(/*EraseFiles=*/false);
1920a518db9SCyndy Ishida   return EXIT_SUCCESS;
1930a518db9SCyndy Ishida }
1940a518db9SCyndy Ishida 
1950a518db9SCyndy Ishida int clang_installapi_main(int argc, char **argv,
1960a518db9SCyndy Ishida                           const llvm::ToolContext &ToolContext) {
1970a518db9SCyndy Ishida   // Standard set up, so program fails gracefully.
1980a518db9SCyndy Ishida   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
1990a518db9SCyndy Ishida   llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
2000a518db9SCyndy Ishida   llvm::llvm_shutdown_obj Shutdown;
2010a518db9SCyndy Ishida 
2020a518db9SCyndy Ishida   if (llvm::sys::Process::FixupStandardFileDescriptors())
2030a518db9SCyndy Ishida     return EXIT_FAILURE;
2040a518db9SCyndy Ishida 
2050a518db9SCyndy Ishida   const char *ProgName =
2060a518db9SCyndy Ishida       ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path;
2070a518db9SCyndy Ishida   return run(llvm::ArrayRef(argv, argc), ProgName);
2080a518db9SCyndy Ishida }
209