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