| //===-- ClangInstallAPI.cpp ----------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This is the entry point to clang-installapi; it is a wrapper |
| // for functionality in the InstallAPI clang library. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Options.h" |
| #include "clang/Basic/Diagnostic.h" |
| #include "clang/Basic/DiagnosticFrontend.h" |
| #include "clang/Driver/DriverDiagnostic.h" |
| #include "clang/Driver/Tool.h" |
| #include "clang/Frontend/TextDiagnosticPrinter.h" |
| #include "clang/InstallAPI/Frontend.h" |
| #include "clang/InstallAPI/FrontendRecords.h" |
| #include "clang/InstallAPI/InstallAPIDiagnostic.h" |
| #include "clang/InstallAPI/MachO.h" |
| #include "clang/Tooling/Tooling.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/Option/Option.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/LLVMDriver.h" |
| #include "llvm/Support/ManagedStatic.h" |
| #include "llvm/Support/PrettyStackTrace.h" |
| #include "llvm/Support/Process.h" |
| #include "llvm/Support/Signals.h" |
| #include "llvm/TargetParser/Host.h" |
| #include <memory> |
| |
| using namespace clang; |
| using namespace clang::installapi; |
| using namespace clang::driver::options; |
| using namespace llvm::opt; |
| using namespace llvm::MachO; |
| |
| static bool runFrontend(StringRef ProgName, Twine Label, bool Verbose, |
| InstallAPIContext &Ctx, |
| llvm::vfs::InMemoryFileSystem *FS, |
| const ArrayRef<std::string> InitialArgs) { |
| |
| std::unique_ptr<llvm::MemoryBuffer> ProcessedInput = createInputBuffer(Ctx); |
| // Skip invoking cc1 when there are no header inputs. |
| if (!ProcessedInput) |
| return true; |
| |
| if (Verbose) |
| llvm::errs() << Label << " Headers:\n" |
| << ProcessedInput->getBuffer() << "\n\n"; |
| |
| std::string InputFile = ProcessedInput->getBufferIdentifier().str(); |
| FS->addFile(InputFile, /*ModTime=*/0, std::move(ProcessedInput)); |
| // Reconstruct arguments with unique values like target triple or input |
| // headers. |
| std::vector<std::string> Args = {ProgName.data(), "-target", |
| Ctx.Slice->getTriple().str().c_str()}; |
| llvm::append_range(Args, InitialArgs); |
| Args.push_back(InputFile); |
| |
| // Create & run invocation. |
| clang::tooling::ToolInvocation Invocation( |
| std::move(Args), std::make_unique<InstallAPIAction>(Ctx), Ctx.FM); |
| return Invocation.run(); |
| } |
| |
| static bool run(ArrayRef<const char *> Args, const char *ProgName) { |
| // Setup Diagnostics engine. |
| DiagnosticOptions DiagOpts; |
| const llvm::opt::OptTable &ClangOpts = clang::driver::getDriverOptTable(); |
| unsigned MissingArgIndex, MissingArgCount; |
| llvm::opt::InputArgList ParsedArgs = ClangOpts.ParseArgs( |
| ArrayRef(Args).slice(1), MissingArgIndex, MissingArgCount); |
| ParseDiagnosticArgs(DiagOpts, ParsedArgs); |
| |
| auto Diag = llvm::makeIntrusiveRefCnt<clang::DiagnosticsEngine>( |
| clang::DiagnosticIDs::create(), DiagOpts, |
| new clang::TextDiagnosticPrinter(llvm::errs(), DiagOpts)); |
| |
| // Create file manager for all file operations and holding in-memory generated |
| // inputs. |
| auto OverlayFileSystem = |
| llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>( |
| llvm::vfs::getRealFileSystem()); |
| auto InMemoryFileSystem = |
| llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); |
| OverlayFileSystem->pushOverlay(InMemoryFileSystem); |
| IntrusiveRefCntPtr<clang::FileManager> FM = |
| llvm::makeIntrusiveRefCnt<FileManager>(clang::FileSystemOptions(), |
| OverlayFileSystem); |
| |
| // Capture all options and diagnose any errors. |
| Options Opts(*Diag, FM.get(), Args, ProgName); |
| if (Diag->hasErrorOccurred()) |
| return EXIT_FAILURE; |
| |
| InstallAPIContext Ctx = Opts.createContext(); |
| if (Diag->hasErrorOccurred()) |
| return EXIT_FAILURE; |
| |
| if (!Opts.DriverOpts.DylibToVerify.empty()) { |
| TargetList Targets; |
| for (const auto &T : Opts.DriverOpts.Targets) |
| Targets.push_back(T.first); |
| if (!Ctx.Verifier->verifyBinaryAttrs(Targets, Ctx.BA, Ctx.Reexports, |
| Opts.LinkerOpts.AllowableClients, |
| Opts.LinkerOpts.RPaths, Ctx.FT)) |
| return EXIT_FAILURE; |
| }; |
| |
| // Set up compilation. |
| std::unique_ptr<CompilerInstance> CI(new CompilerInstance()); |
| CI->setFileManager(FM); |
| CI->createDiagnostics(FM->getVirtualFileSystem()); |
| if (!CI->hasDiagnostics()) |
| return EXIT_FAILURE; |
| |
| // Execute, verify and gather AST results. |
| // An invocation is ran for each unique target triple and for each header |
| // access level. |
| Records FrontendRecords; |
| for (const auto &[Targ, Trip] : Opts.DriverOpts.Targets) { |
| Ctx.Verifier->setTarget(Targ); |
| Ctx.Slice = std::make_shared<FrontendRecordsSlice>(Trip); |
| for (const HeaderType Type : |
| {HeaderType::Public, HeaderType::Private, HeaderType::Project}) { |
| std::vector<std::string> ArgStrings = Opts.getClangFrontendArgs(); |
| Opts.addConditionalCC1Args(ArgStrings, Trip, Type); |
| Ctx.Type = Type; |
| StringRef HeaderLabel = getName(Ctx.Type); |
| if (!runFrontend(ProgName, HeaderLabel, Opts.DriverOpts.Verbose, Ctx, |
| InMemoryFileSystem.get(), ArgStrings)) |
| return EXIT_FAILURE; |
| |
| // Run extra passes for unique compiler arguments. |
| for (const auto &[Label, ExtraArgs] : Opts.FEOpts.UniqueArgs) { |
| std::vector<std::string> FinalArguments = ArgStrings; |
| llvm::append_range(FinalArguments, ExtraArgs); |
| if (!runFrontend(ProgName, Label + " " + HeaderLabel, |
| Opts.DriverOpts.Verbose, Ctx, InMemoryFileSystem.get(), |
| FinalArguments)) |
| return EXIT_FAILURE; |
| } |
| } |
| FrontendRecords.emplace_back(std::move(Ctx.Slice)); |
| } |
| |
| if (Ctx.Verifier->verifyRemainingSymbols() == DylibVerifier::Result::Invalid) |
| return EXIT_FAILURE; |
| |
| // After symbols have been collected, prepare to write output. |
| auto Out = CI->createOutputFile(Ctx.OutputLoc, /*Binary=*/false, |
| /*RemoveFileOnSignal=*/false, |
| /*UseTemporary=*/false, |
| /*CreateMissingDirectories=*/false); |
| if (!Out) |
| return EXIT_FAILURE; |
| |
| // Assign attributes for serialization. |
| InterfaceFile IF(Ctx.Verifier->takeExports()); |
| // Assign attributes that are the same per slice first. |
| for (const auto &TargetInfo : Opts.DriverOpts.Targets) { |
| IF.addTarget(TargetInfo.first); |
| IF.setFromBinaryAttrs(Ctx.BA, TargetInfo.first); |
| } |
| // Then assign potentially different attributes per slice after. |
| auto assignLibAttrs = |
| [&IF]( |
| const auto &Attrs, |
| std::function<void(InterfaceFile *, StringRef, const Target &)> Add) { |
| for (const auto &[Attr, ArchSet] : Attrs.get()) |
| for (const auto &T : IF.targets(ArchSet)) |
| Add(&IF, Attr, T); |
| }; |
| |
| assignLibAttrs(Opts.LinkerOpts.AllowableClients, |
| &InterfaceFile::addAllowableClient); |
| assignLibAttrs(Opts.LinkerOpts.RPaths, &InterfaceFile::addRPath); |
| assignLibAttrs(Ctx.Reexports, &InterfaceFile::addReexportedLibrary); |
| |
| // Write output file and perform CI cleanup. |
| if (auto Err = TextAPIWriter::writeToStream(*Out, IF, Ctx.FT)) { |
| Diag->Report(diag::err_cannot_write_file) |
| << Ctx.OutputLoc << std::move(Err); |
| CI->clearOutputFiles(/*EraseFiles=*/true); |
| return EXIT_FAILURE; |
| } |
| |
| CI->clearOutputFiles(/*EraseFiles=*/false); |
| return EXIT_SUCCESS; |
| } |
| |
| int clang_installapi_main(int argc, char **argv, |
| const llvm::ToolContext &ToolContext) { |
| // Standard set up, so program fails gracefully. |
| llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); |
| llvm::PrettyStackTraceProgram StackPrinter(argc, argv); |
| llvm::llvm_shutdown_obj Shutdown; |
| |
| if (llvm::sys::Process::FixupStandardFileDescriptors()) |
| return EXIT_FAILURE; |
| |
| const char *ProgName = |
| ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path; |
| return run(llvm::ArrayRef(argv, argc), ProgName); |
| } |