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