https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/81571
>From 0f938ae8480f74a44bf6fe4aac6105e457315bb9 Mon Sep 17 00:00:00 2001 From: Cyndy Ishida <cyndy_ish...@apple.com> Date: Mon, 12 Feb 2024 20:53:25 -0800 Subject: [PATCH 1/2] [clang][InstallAPI] Introduce basic driver to write out tbd files. This introduces a basic outline of installapi as a clang driver option. It captures relavant information as cc1 args, which is commonly arguments already passed to the linker to encode into TBD file outputs. This is effectively an upstream for what already exists as `tapi installapi` in Xcode toolchains, but direclty in clang. This patch does not handle any AST traversing on input yet. InstallAPI is broadly an operation thats takes a series of header files that represent a single dynamic library and generates a TBD file out of it which represents all the linkable symbols and necessary attributes for statically linking in clients. It also will support verification where it compares all the information recorded for the TBD file against the already built binary, to catch possible mismatches like when an symbol export is missing a matching declaration. --- .../clang/Basic/DiagnosticDriverKinds.td | 3 + clang/include/clang/Driver/Action.h | 12 ++++ clang/include/clang/Driver/Options.td | 12 +++- clang/include/clang/Driver/Types.def | 1 + .../include/clang/Frontend/CompilerInstance.h | 7 ++ .../clang/Frontend/CompilerInvocation.h | 9 ++- .../include/clang/Frontend/FrontendActions.h | 10 +++ .../include/clang/Frontend/FrontendOptions.h | 3 + .../clang/Frontend/InstallAPIOptions.h | 28 ++++++++ clang/include/clang/InstallAPI/Context.h | 65 +++++++++++++++++++ clang/lib/CMakeLists.txt | 1 + clang/lib/Driver/Action.cpp | 7 ++ clang/lib/Driver/Driver.cpp | 15 ++++- clang/lib/Driver/ToolChain.cpp | 1 + clang/lib/Driver/ToolChains/Clang.cpp | 11 ++++ clang/lib/Frontend/CMakeLists.txt | 2 + clang/lib/Frontend/CompilerInvocation.cpp | 41 +++++++++++- clang/lib/Frontend/InstallAPIConsumer.cpp | 43 ++++++++++++ .../ExecuteCompilerInvocation.cpp | 2 + clang/lib/InstallAPI/CMakeLists.txt | 11 ++++ clang/lib/InstallAPI/Context.cpp | 27 ++++++++ clang/test/CMakeLists.txt | 1 + clang/test/Driver/installapi.h | 13 ++++ clang/test/InstallAPI/installapi-basic.test | 34 ++++++++++ clang/test/lit.cfg.py | 2 + 25 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 clang/include/clang/Frontend/InstallAPIOptions.h create mode 100644 clang/include/clang/InstallAPI/Context.h create mode 100644 clang/lib/Frontend/InstallAPIConsumer.cpp create mode 100644 clang/lib/InstallAPI/CMakeLists.txt create mode 100644 clang/lib/InstallAPI/Context.cpp create mode 100644 clang/test/Driver/installapi.h create mode 100644 clang/test/InstallAPI/installapi-basic.test diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index b13181f6e70894..24cc17420c16cc 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -804,4 +804,7 @@ def warn_android_unversioned_fallback : Warning< def err_drv_triple_version_invalid : Error< "version '%0' in target triple '%1' is invalid">; + +def err_drv_installapi_unsupported : Error< + "the clang compiler does not support '%0' for InstallAPI">; } diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h index 04fa8b01b418f8..2768e2f5df1a9e 100644 --- a/clang/include/clang/Driver/Action.h +++ b/clang/include/clang/Driver/Action.h @@ -59,6 +59,7 @@ class Action { PreprocessJobClass, PrecompileJobClass, ExtractAPIJobClass, + InstallAPIJobClass, AnalyzeJobClass, MigrateJobClass, CompileJobClass, @@ -448,6 +449,17 @@ class ExtractAPIJobAction : public JobAction { void addHeaderInput(Action *Input) { getInputs().push_back(Input); } }; +class InstallAPIJobAction : public JobAction { + void anchor() override; + +public: + InstallAPIJobAction(Action *Input, types::ID OutputType); + + static bool classof(const Action *A) { + return A->getKind() == InstallAPIJobClass; + } +}; + class AnalyzeJobAction : public JobAction { void anchor() override; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 31e8571758bfce..52c892b8b7f82c 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -309,6 +309,8 @@ class AnalyzerOpts<string base> : KeyPathAndMacro<"AnalyzerOpts->", base, "ANALYZER_"> {} class MigratorOpts<string base> : KeyPathAndMacro<"MigratorOpts.", base, "MIGRATOR_"> {} +class InstallAPIOpts<string base> + : KeyPathAndMacro<"InstallAPIOpts.", base, "INSTALLAPI_"> {} // A boolean option which is opt-in in CC1. The positive option exists in CC1 and // Args.hasArg(OPT_ffoo) can be used to check that the flag is enabled. @@ -1114,7 +1116,8 @@ def config_user_dir_EQ : Joined<["--"], "config-user-dir=">, def coverage : Flag<["-", "--"], "coverage">, Group<Link_Group>, Visibility<[ClangOption, CLOption]>; def cpp_precomp : Flag<["-"], "cpp-precomp">, Group<clang_ignored_f_Group>; -def current__version : JoinedOrSeparate<["-"], "current_version">; +def current__version : JoinedOrSeparate<["-"], "current_version">, + Visibility<[ClangOption, CC1Option]>; def cxx_isystem : JoinedOrSeparate<["-"], "cxx-isystem">, Group<clang_i_Group>, HelpText<"Add directory to the C++ SYSTEM include search path">, Visibility<[ClangOption, CC1Option]>, @@ -1529,6 +1532,9 @@ def static_libsan : Flag<["-"], "static-libsan">, HelpText<"Statically link the sanitizer runtime (Not supported for ASan, TSan or UBSan on darwin)">; def : Flag<["-"], "shared-libasan">, Alias<shared_libsan>; def fasm : Flag<["-"], "fasm">, Group<f_Group>; +def installapi : Flag<["-"], "installapi">, + Visibility<[ClangOption, CC1Option]>, Group<Action_Group>, + HelpText<"Create a text-based stub file by scanning header files">; defm assume_unique_vtables : BoolFOption<"assume-unique-vtables", CodeGenOpts<"AssumeUniqueVTables">, DefaultTrue, @@ -4291,7 +4297,9 @@ def verify_pch : Flag<["-"], "verify-pch">, Group<Action_Group>, Visibility<[ClangOption, CC1Option]>, HelpText<"Load and verify that a pre-compiled header file is not stale">; def init : Separate<["-"], "init">; -def install__name : Separate<["-"], "install_name">; +def install__name : Separate<["-"], "install_name">, + Visibility<[ClangOption, CC1Option]>, + MarshallingInfoString<InstallAPIOpts<"InstallName">>; def iprefix : JoinedOrSeparate<["-"], "iprefix">, Group<clang_i_Group>, Visibility<[ClangOption, CC1Option]>, HelpText<"Set the -iwithprefix/-iwithprefixbefore prefix">, MetaVarName<"<dir>">; diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def index f72c27e1ee7019..2089eb4c5f8649 100644 --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -94,6 +94,7 @@ TYPE("lto-bc", LTO_BC, INVALID, "o", phases TYPE("ast", AST, INVALID, "ast", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("ifs", IFS, INVALID, "ifs", phases::IfsMerge) TYPE("ifs-cpp", IFS_CPP, INVALID, "ifs", phases::Compile, phases::IfsMerge) +TYPE("tbd", TextAPI, INVALID, "tbd", phases::Compile) TYPE("pcm", ModuleFile, INVALID, "pcm", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("header-unit", HeaderUnit, INVALID, "pcm", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("plist", Plist, INVALID, "plist", phases::Compile, phases::Backend, phases::Assemble, phases::Link) diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index ac2f940769fbe9..6eb7972f86ca5b 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -294,6 +294,13 @@ class CompilerInstance : public ModuleLoader { return Invocation->getFrontendOpts(); } + InstallAPIOptions &getInstallAPIOpts() { + return Invocation->getInstallAPIOpts(); + } + const InstallAPIOptions &getInstallAPIOpts() const { + return Invocation->getInstallAPIOpts(); + } + HeaderSearchOptions &getHeaderSearchOpts() { return Invocation->getHeaderSearchOpts(); } diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index c6528779bde7b2..a01d9695dce203 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -18,11 +18,12 @@ #include "clang/Basic/LangStandard.h" #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/InstallAPIOptions.h" #include "clang/Frontend/MigratorOptions.h" #include "clang/Frontend/PreprocessorOutputOptions.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include <memory> #include <string> @@ -111,6 +112,9 @@ class CompilerInvocationBase { /// Options controlling preprocessed output. std::shared_ptr<PreprocessorOutputOptions> PreprocessorOutputOpts; + /// Options controlling InstallAPI operations and output. + std::shared_ptr<InstallAPIOptions> InstallAPIOpts; + /// Dummy tag type whose instance can be passed into the constructor to /// prevent creation of the reference-counted option objects. struct EmptyConstructor {}; @@ -145,6 +149,7 @@ class CompilerInvocationBase { const PreprocessorOutputOptions &getPreprocessorOutputOpts() const { return *PreprocessorOutputOpts; } + const InstallAPIOptions &getInstallAPIOpts() const { return *InstallAPIOpts; } /// @} /// Command line generation. @@ -237,6 +242,7 @@ class CompilerInvocation : public CompilerInvocationBase { using CompilerInvocationBase::getFrontendOpts; using CompilerInvocationBase::getDependencyOutputOpts; using CompilerInvocationBase::getPreprocessorOutputOpts; + using CompilerInvocationBase::getInstallAPIOpts; /// @} /// Mutable getters. @@ -258,6 +264,7 @@ class CompilerInvocation : public CompilerInvocationBase { PreprocessorOutputOptions &getPreprocessorOutputOpts() { return *PreprocessorOutputOpts; } + InstallAPIOptions &getInstallAPIOpts() { return *InstallAPIOpts; } /// @} /// Base class internals. diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h index fcce31ac0590ff..b8229252f5ed22 100644 --- a/clang/include/clang/Frontend/FrontendActions.h +++ b/clang/include/clang/Frontend/FrontendActions.h @@ -130,6 +130,16 @@ class GenerateModuleAction : public ASTFrontendAction { bool shouldEraseOutputFiles() override; }; +class InstallAPIAction : public ASTFrontendAction { +protected: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + +public: + static std::unique_ptr<llvm::raw_pwrite_stream> + CreateOutputFile(CompilerInstance &CI, StringRef InFile); +}; + class GenerateInterfaceStubsAction : public ASTFrontendAction { protected: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index 53a8681cfdbba0..62d16ba542ea4d 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -100,6 +100,9 @@ enum ActionKind { /// Only execute frontend initialization. InitOnly, + // Create TextAPI stub. + InstallAPI, + /// Dump information about a module file. ModuleFileInfo, diff --git a/clang/include/clang/Frontend/InstallAPIOptions.h b/clang/include/clang/Frontend/InstallAPIOptions.h new file mode 100644 index 00000000000000..cf65a3350c6de6 --- /dev/null +++ b/clang/include/clang/Frontend/InstallAPIOptions.h @@ -0,0 +1,28 @@ +//===--- InstallAPIOptions.h ------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_INSTALLAPIOPTIONS_H +#define LLVM_CLANG_FRONTEND_INSTALLAPIOPTIONS_H + +#include "llvm/TextAPI/PackedVersion.h" + +namespace clang { + +/// InstallAPIOptions - Options for controlling InstallAPI verification and +/// TextAPI output. +class InstallAPIOptions { +public: + /// The install name which is apart of the library's ID. + std::string InstallName; + + /// The current version which is apart of the library's ID. + llvm::MachO::PackedVersion CurrentVersion; +}; +} // namespace clang + +#endif diff --git a/clang/include/clang/InstallAPI/Context.h b/clang/include/clang/InstallAPI/Context.h new file mode 100644 index 00000000000000..a1ff7c12a2f835 --- /dev/null +++ b/clang/include/clang/InstallAPI/Context.h @@ -0,0 +1,65 @@ +//===- InstallAPI/Context.h -------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Top level types for interacting with the generic clang driver and frontend +// for InstallAPI operations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INSTALLAPI_CONTEXT_H +#define LLVM_CLANG_INSTALLAPI_CONTEXT_H + +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/RecordVisitor.h" +#include "llvm/TextAPI/RecordsSlice.h" + +namespace clang { +namespace installapi { + +/// Struct used for generating validating InstallAPI. +/// The attributes captured represent all necessary information +/// to generate TextAPI output. +struct InstallAPIContext { + + /// Library attributes that are typically passed as linker inputs. + llvm::MachO::RecordsSlice::BinaryAttrs BA; + + /// Active target triple to parse. + llvm::Triple TargetTriple{}; + + /// Output stream to write TextAPI file to. + std::unique_ptr<llvm::raw_pwrite_stream> OS = nullptr; + + /// DiagnosticsEngine to report errors. + llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags = nullptr; + + /// File Path of output location. + StringRef OutputLoc{}; + + /// What encoding to write output as. + llvm::MachO::FileType FT = llvm::MachO::FileType::TBD_V5; +}; + +class InstallAPIConsumer : public ASTConsumer { +public: + InstallAPIConsumer(InstallAPIContext InstallAPICtx) + : Ctx(std::move(InstallAPICtx)) {} + + void HandleTranslationUnit(ASTContext &ASTContext) override; + +private: + InstallAPIContext Ctx; +}; + +} // namespace installapi +} // namespace clang + +#endif // LLVM_CLANG_INSTALLAPI_CONTEXT_H diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt index 1526d65795f8ad..0cac86451f39e4 100644 --- a/clang/lib/CMakeLists.txt +++ b/clang/lib/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(Tooling) add_subdirectory(DirectoryWatcher) add_subdirectory(Index) add_subdirectory(IndexSerialization) +add_subdirectory(InstallAPI) add_subdirectory(StaticAnalyzer) add_subdirectory(Format) if(CLANG_INCLUDE_TESTS) diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp index 849bf6035ebd2e..7b1a1bb0228c41 100644 --- a/clang/lib/Driver/Action.cpp +++ b/clang/lib/Driver/Action.cpp @@ -32,6 +32,8 @@ const char *Action::getClassName(ActionClass AC) { case CompileJobClass: return "compiler"; case BackendJobClass: return "backend"; case AssembleJobClass: return "assembler"; + case InstallAPIJobClass: + return "installapi"; case IfsMergeJobClass: return "interface-stub-merger"; case LinkJobClass: return "linker"; case LipoJobClass: return "lipo"; @@ -362,6 +364,11 @@ void ExtractAPIJobAction::anchor() {} ExtractAPIJobAction::ExtractAPIJobAction(Action *Inputs, types::ID OutputType) : JobAction(ExtractAPIJobClass, Inputs, OutputType) {} +void InstallAPIJobAction::anchor() {} + +InstallAPIJobAction::InstallAPIJobAction(Action *Inputs, types::ID OutputType) + : JobAction(InstallAPIJobClass, Inputs, OutputType) {} + void AnalyzeJobAction::anchor() {} AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 00e14071a4afec..f5497c0c306190 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -4189,6 +4189,11 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, break; } + if (auto *IAA = dyn_cast<InstallAPIJobAction>(Current)) { + Current = nullptr; + break; + } + // FIXME: Should we include any prior module file outputs as inputs of // later actions in the same command line? @@ -4319,6 +4324,12 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, if (!MergerInputs.empty()) Actions.push_back( C.MakeAction<IfsMergeJobAction>(MergerInputs, types::TY_Image)); + } else if (Args.hasArg(options::OPT_installapi)) { + assert(Inputs.size() == 1 && "InstallAPI action can only handle 1 input"); + const auto [InputType, InputArg] = Inputs.front(); + Action *Current = C.MakeAction<InputAction>(*InputArg, InputType); + Actions.push_back( + C.MakeAction<InstallAPIJobAction>(Current, types::TY_TextAPI)); } for (auto Opt : {options::OPT_print_supported_cpus, @@ -4762,6 +4773,8 @@ Action *Driver::ConstructPhaseAction( return C.MakeAction<VerifyPCHJobAction>(Input, types::TY_Nothing); if (Args.hasArg(options::OPT_extract_api)) return C.MakeAction<ExtractAPIJobAction>(Input, types::TY_API_INFO); + if (Args.hasArg(options::OPT_installapi)) + return C.MakeAction<InstallAPIJobAction>(Input, types::TY_TextAPI); return C.MakeAction<CompileJobAction>(Input, types::TY_LLVM_BC); } case phases::Backend: { @@ -6441,7 +6454,7 @@ bool Driver::ShouldUseClangCompiler(const JobAction &JA) const { // And say "no" if this is not a kind of action clang understands. if (!isa<PreprocessJobAction>(JA) && !isa<PrecompileJobAction>(JA) && !isa<CompileJobAction>(JA) && !isa<BackendJobAction>(JA) && - !isa<ExtractAPIJobAction>(JA)) + !isa<ExtractAPIJobAction>(JA) && !isa<InstallAPIJobAction>(JA)) return false; return true; diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 388030592b4836..657577cea6c7d8 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -532,6 +532,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const { case Action::PrecompileJobClass: case Action::PreprocessJobClass: case Action::ExtractAPIJobClass: + case Action::InstallAPIJobClass: case Action::AnalyzeJobClass: case Action::MigrateJobClass: case Action::VerifyPCHJobClass: diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 4459d86e77d5d9..47305f798c5fee 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4939,6 +4939,17 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Arg *ExtractAPIIgnoresFileArg = Args.getLastArg(options::OPT_extract_api_ignores_EQ)) ExtractAPIIgnoresFileArg->render(Args, CmdArgs); + } else if (isa<InstallAPIJobAction>(JA)) { + if (!Triple.isOSDarwin()) + D.Diag(diag::err_drv_installapi_unsupported) << Triple.str(); + + CmdArgs.push_back("-installapi"); + // Add necessary library arguments for InstallAPI. + if (const Arg *A = Args.getLastArg(options::OPT_install__name)) + A->render(Args, CmdArgs); + if (const Arg *A = Args.getLastArg(options::OPT_current__version)) + A->render(Args, CmdArgs); + } else { assert((isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) && "Invalid action for clang tool."); diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index a9166672088459..ce2a76f6c37910 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -27,6 +27,7 @@ add_clang_library(clangFrontend HeaderIncludeGen.cpp InitPreprocessor.cpp LayoutOverrideSource.cpp + InstallAPIConsumer.cpp LogDiagnosticPrinter.cpp ModuleDependencyCollector.cpp MultiplexConsumer.cpp @@ -53,6 +54,7 @@ add_clang_library(clangFrontend clangBasic clangDriver clangEdit + clangInstallAPI clangLex clangParse clangSema diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 8d7b75b56d6129..bcb31243056b7e 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -149,7 +149,8 @@ CompilerInvocationBase::CompilerInvocationBase() FSOpts(std::make_shared<FileSystemOptions>()), FrontendOpts(std::make_shared<FrontendOptions>()), DependencyOutputOpts(std::make_shared<DependencyOutputOptions>()), - PreprocessorOutputOpts(std::make_shared<PreprocessorOutputOptions>()) {} + PreprocessorOutputOpts(std::make_shared<PreprocessorOutputOptions>()), + InstallAPIOpts(std::make_shared<InstallAPIOptions>()) {} CompilerInvocationBase & CompilerInvocationBase::deep_copy_assign(const CompilerInvocationBase &X) { @@ -167,6 +168,7 @@ CompilerInvocationBase::deep_copy_assign(const CompilerInvocationBase &X) { FrontendOpts = make_shared_copy(X.getFrontendOpts()); DependencyOutputOpts = make_shared_copy(X.getDependencyOutputOpts()); PreprocessorOutputOpts = make_shared_copy(X.getPreprocessorOutputOpts()); + InstallAPIOpts = make_shared_copy(X.getInstallAPIOpts()); } return *this; } @@ -187,6 +189,7 @@ CompilerInvocationBase::shallow_copy_assign(const CompilerInvocationBase &X) { FrontendOpts = X.FrontendOpts; DependencyOutputOpts = X.DependencyOutputOpts; PreprocessorOutputOpts = X.PreprocessorOutputOpts; + InstallAPIOpts = X.InstallAPIOpts; } return *this; } @@ -2158,6 +2161,34 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, return Diags.getNumErrors() == NumErrorsBefore; } +static bool ParseInstallAPIArgs(InstallAPIOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, + frontend::ActionKind Action) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + InstallAPIOptions &InstallAPIOpts = Opts; +#define INSTALLAPI_OPTION_WITH_MARSHALLING(...) \ + PARSE_OPTION_WITH_MARSHALLING(Args, Diags, __VA_ARGS__) +#include "clang/Driver/Options.inc" +#undef INSTALLAPI_OPTION_WITH_MARSHALLING + if (Arg *A = Args.getLastArg(options::OPT_current__version)) + Opts.CurrentVersion.parse64(A->getValue()); + + return Diags.getNumErrors() == NumErrorsBefore; +} + +static void GenerateInstallAPIArgs(const InstallAPIOptions &Opts, + ArgumentConsumer Consumer) { + const InstallAPIOptions &InstallAPIOpts = Opts; +#define INSTALLAPI_OPTION_WITH_MARSHALLING(...) \ + GENERATE_OPTION_WITH_MARSHALLING(Consumer, __VA_ARGS__) +#include "clang/Driver/Options.inc" +#undef INSTALLAPI_OPTION_WITH_MARSHALLING + if (!Opts.CurrentVersion.empty()) + GenerateArg(Consumer, OPT_current__version, + std::string(Opts.CurrentVersion)); +} + static void GenerateDependencyOutputArgs(const DependencyOutputOptions &Opts, ArgumentConsumer Consumer) { const DependencyOutputOptions &DependencyOutputOpts = Opts; @@ -2557,6 +2588,7 @@ static const auto &getFrontendActionTable() { {frontend::GeneratePCH, OPT_emit_pch}, {frontend::GenerateInterfaceStubs, OPT_emit_interface_stubs}, {frontend::InitOnly, OPT_init_only}, + {frontend::InstallAPI, OPT_installapi}, {frontend::ParseSyntaxOnly, OPT_fsyntax_only}, {frontend::ModuleFileInfo, OPT_module_file_info}, {frontend::VerifyPCH, OPT_verify_pch}, @@ -4280,6 +4312,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { case frontend::GenerateHeaderUnit: case frontend::GeneratePCH: case frontend::GenerateInterfaceStubs: + case frontend::InstallAPI: case frontend::ParseSyntaxOnly: case frontend::ModuleFileInfo: case frontend::VerifyPCH: @@ -4654,6 +4687,11 @@ bool CompilerInvocation::CreateFromArgsImpl( Res.getDependencyOutputOpts().Targets.empty()) Diags.Report(diag::err_fe_dependency_file_requires_MT); + if (Args.hasArg(OPT_installapi)) { + ParseInstallAPIArgs(Res.getInstallAPIOpts(), Args, Diags, + Res.getFrontendOpts().ProgramAction); + } + // If sanitizer is enabled, disable OPT_ffine_grained_bitfield_accesses. if (Res.getCodeGenOpts().FineGrainedBitfieldAccesses && !Res.getLangOpts().Sanitize.empty()) { @@ -4844,6 +4882,7 @@ void CompilerInvocationBase::generateCC1CommandLine( GeneratePreprocessorOutputArgs(getPreprocessorOutputOpts(), Consumer, getFrontendOpts().ProgramAction); GenerateDependencyOutputArgs(getDependencyOutputOpts(), Consumer); + GenerateInstallAPIArgs(getInstallAPIOpts(), Consumer); } std::vector<std::string> CompilerInvocationBase::getCC1CommandLine() const { diff --git a/clang/lib/Frontend/InstallAPIConsumer.cpp b/clang/lib/Frontend/InstallAPIConsumer.cpp new file mode 100644 index 00000000000000..c0f22c1a589f38 --- /dev/null +++ b/clang/lib/Frontend/InstallAPIConsumer.cpp @@ -0,0 +1,43 @@ +//===--- InstallAPIConsumer.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 +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/InstallAPI/Context.h" + +using namespace clang; +using namespace clang::installapi; + +std::unique_ptr<ASTConsumer> +InstallAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + const InstallAPIOptions &Opts = CI.getInstallAPIOpts(); + InstallAPIContext Ctx; + Ctx.BA.InstallName = Opts.InstallName; + Ctx.BA.AppExtensionSafe = CI.getLangOpts().AppExt; + Ctx.BA.CurrentVersion = Opts.CurrentVersion; + // InstallAPI requires two level namespacing. + Ctx.BA.TwoLevelNamespace = true; + Ctx.TargetTriple = CI.getTarget().getTriple(); + + Ctx.Diags = &CI.getDiagnostics(); + Ctx.OutputLoc = CI.getFrontendOpts().OutputFile; + Ctx.OS = CreateOutputFile(CI, InFile); + if (!Ctx.OS) + return nullptr; + return std::make_unique<InstallAPIConsumer>(std::move(Ctx)); +} + +std::unique_ptr<llvm::raw_pwrite_stream> +InstallAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { + std::unique_ptr<raw_pwrite_stream> OS = + CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"tbd", + /*RemoveFileOnSignal=*/false); + if (!OS) + return nullptr; + return OS; +} diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 925879a68cbd09..a47c474e520a01 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -71,6 +71,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) { case GenerateInterfaceStubs: return std::make_unique<GenerateInterfaceStubsAction>(); case InitOnly: return std::make_unique<InitOnlyAction>(); + case InstallAPI: + return std::make_unique<InstallAPIAction>(); case ParseSyntaxOnly: return std::make_unique<SyntaxOnlyAction>(); case ModuleFileInfo: return std::make_unique<DumpModuleInfoAction>(); case VerifyPCH: return std::make_unique<VerifyPCHAction>(); diff --git a/clang/lib/InstallAPI/CMakeLists.txt b/clang/lib/InstallAPI/CMakeLists.txt new file mode 100644 index 00000000000000..b68d8fbbec1d37 --- /dev/null +++ b/clang/lib/InstallAPI/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + Support + TextAPI + ) + +add_clang_library(clangInstallAPI + Context.cpp + + LINK_LIBS + clangAST + ) diff --git a/clang/lib/InstallAPI/Context.cpp b/clang/lib/InstallAPI/Context.cpp new file mode 100644 index 00000000000000..d4df52f66560c1 --- /dev/null +++ b/clang/lib/InstallAPI/Context.cpp @@ -0,0 +1,27 @@ +//===--- InstallAPI/Context.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 +// +//===----------------------------------------------------------------------===// + +#include "clang/InstallAPI/Context.h" +#include "clang/AST/ASTContext.h" +#include "llvm/TextAPI/TextAPIWriter.h" + +using namespace clang; +using namespace clang::installapi; +using namespace llvm::MachO; + +void InstallAPIConsumer::HandleTranslationUnit(ASTContext &Context) { + if (Context.getDiagnostics().hasErrorOccurred()) + return; + InterfaceFile IF; + // Set library attributes captured through cc1 args. + Target T(Ctx.TargetTriple); + IF.addTarget(T); + IF.setFromBinaryAttrs(Ctx.BA, T); + if (auto Err = TextAPIWriter::writeToStream(*Ctx.OS, IF, Ctx.FT)) + Ctx.Diags->Report(diag::err_cannot_open_file) << Ctx.OutputLoc; +} diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index f17ded42a019c2..841317cef880a2 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -131,6 +131,7 @@ if( NOT CLANG_BUILT_STANDALONE ) llvm-rc llvm-readelf llvm-readobj + llvm-readtapi llvm-strip llvm-symbolizer llvm-windres diff --git a/clang/test/Driver/installapi.h b/clang/test/Driver/installapi.h new file mode 100644 index 00000000000000..14586ea600cc46 --- /dev/null +++ b/clang/test/Driver/installapi.h @@ -0,0 +1,13 @@ +// Check non-darwin triple is rejected. +// RUN: not %clang -target x86_64-unknown-unknown -installapi %s 2> %t +// RUN: FileCheck --check-prefix INVALID_INSTALLAPI -input-file %t %s + +// INVALID_INSTALLAPI: error: the clang compiler does not support 'x86_64-unknown-unknown' for InstallAPI + +// Check installapi phases. +// RUN: %clang -target x86_64-apple-macos11 -ccc-print-phases -installapi %s 2> %t +// RUN: FileCheck --check-prefix INSTALLAPI_PHASES -input-file %t %s + +// INSTALLAPI_PHASES: 0: input, +// INSTALLAPI_PHASES: installapi, +// INSTALLAPI_PHASES-SAME: tbd diff --git a/clang/test/InstallAPI/installapi-basic.test b/clang/test/InstallAPI/installapi-basic.test new file mode 100644 index 00000000000000..8035166d076dab --- /dev/null +++ b/clang/test/InstallAPI/installapi-basic.test @@ -0,0 +1,34 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -x objective-c -triple arm64-apple-ios13.0.0 -installapi \ +// RUN: -fapplication-extension -current_version 1 -install_name /usr/lib/basic.dylib \ +// RUN: %t/basic_inputs.json -o %t/basic.tbd 2>&1 | FileCheck %s --allow-empty +// RUN: llvm-readtapi -compare %t/basic.tbd %t/expected.tbd 2>&1 | FileCheck %s --allow-empty + +// CHECK-NOT: error: +// CHECK-NOT: warning: + +//--- basic_inputs.json + +//--- expected.tbd +{ + "main_library": { + "compatibility_versions": [ + { + "version": "0" + } + ], + "install_names": [ + { + "name": "/usr/lib/basic.dylib" + } + ], + "target_info": [ + { + "min_deployment": "13.0.0", + "target": "arm64-ios" + } + ] + }, + "tapi_tbd_version": 5 +} diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 271372b928ac55..23818aa62cc05a 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -45,6 +45,7 @@ ".rs", ".ifs", ".rc", + ".tbd", ] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' @@ -97,6 +98,7 @@ "llvm-lto", "llvm-lto2", "llvm-profdata", + "llvm-readtapi", ToolSubst( "%clang_extdef_map", command=FindTool("clang-extdef-mapping"), >From 1d57da5399d6fdb4097dd262866d5a5055284d90 Mon Sep 17 00:00:00 2001 From: Cyndy Ishida <cyndy_ish...@apple.com> Date: Tue, 13 Feb 2024 14:44:52 -0800 Subject: [PATCH 2/2] Address review comments --- clang/include/clang/Basic/DiagnosticDriverKinds.td | 2 +- clang/include/clang/Driver/Types.def | 2 +- clang/lib/Driver/Driver.cpp | 1 + clang/lib/Frontend/CMakeLists.txt | 1 + clang/test/Driver/installapi.h | 2 +- clang/test/lit.cfg.py | 1 - 6 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 24cc17420c16cc..0807d8877591a6 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -806,5 +806,5 @@ def err_drv_triple_version_invalid : Error< "version '%0' in target triple '%1' is invalid">; def err_drv_installapi_unsupported : Error< - "the clang compiler does not support '%0' for InstallAPI">; + "InstallAPI is not supported for '%0'">; } diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def index 2089eb4c5f8649..570a53441d1c74 100644 --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -94,7 +94,7 @@ TYPE("lto-bc", LTO_BC, INVALID, "o", phases TYPE("ast", AST, INVALID, "ast", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("ifs", IFS, INVALID, "ifs", phases::IfsMerge) TYPE("ifs-cpp", IFS_CPP, INVALID, "ifs", phases::Compile, phases::IfsMerge) -TYPE("tbd", TextAPI, INVALID, "tbd", phases::Compile) +TYPE("tbd", TextAPI, INVALID, "tbd", phases::Precompile) TYPE("pcm", ModuleFile, INVALID, "pcm", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("header-unit", HeaderUnit, INVALID, "pcm", phases::Compile, phases::Backend, phases::Assemble, phases::Link) TYPE("plist", Plist, INVALID, "plist", phases::Compile, phases::Backend, phases::Assemble, phases::Link) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index f5497c0c306190..cf84ef21dfa8ce 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -4325,6 +4325,7 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, Actions.push_back( C.MakeAction<IfsMergeJobAction>(MergerInputs, types::TY_Image)); } else if (Args.hasArg(options::OPT_installapi)) { + // TODO: Lift restriction once operation can handle multiple inputs. assert(Inputs.size() == 1 && "InstallAPI action can only handle 1 input"); const auto [InputType, InputArg] = Inputs.front(); Action *Current = C.MakeAction<InputAction>(*InputArg, InputType); diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index ce2a76f6c37910..f443d88b5d30cb 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS ProfileData Support TargetParser + TextAPI ) add_clang_library(clangFrontend diff --git a/clang/test/Driver/installapi.h b/clang/test/Driver/installapi.h index 14586ea600cc46..99379b44d1379c 100644 --- a/clang/test/Driver/installapi.h +++ b/clang/test/Driver/installapi.h @@ -2,7 +2,7 @@ // RUN: not %clang -target x86_64-unknown-unknown -installapi %s 2> %t // RUN: FileCheck --check-prefix INVALID_INSTALLAPI -input-file %t %s -// INVALID_INSTALLAPI: error: the clang compiler does not support 'x86_64-unknown-unknown' for InstallAPI +// INVALID_INSTALLAPI: error: InstallAPI is not supported for 'x86_64-unknown-unknown' // Check installapi phases. // RUN: %clang -target x86_64-apple-macos11 -ccc-print-phases -installapi %s 2> %t diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 23818aa62cc05a..f93b5d9c945886 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -45,7 +45,6 @@ ".rs", ".ifs", ".rc", - ".tbd", ] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits