diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index b13181f6e70894..0807d8877591a6 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< + "InstallAPI is not supported for '%0'">; } 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 c625d0dd1c0c72..95b464e7d61834 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -309,6 +309,8 @@ class AnalyzerOpts : KeyPathAndMacro<"AnalyzerOpts->", base, "ANALYZER_"> {} class MigratorOpts : KeyPathAndMacro<"MigratorOpts.", base, "MIGRATOR_"> {} +class InstallAPIOpts + : 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, Visibility<[ClangOption, CLOption]>; def cpp_precomp : Flag<["-"], "cpp-precomp">, Group; -def current__version : JoinedOrSeparate<["-"], "current_version">; +def current__version : JoinedOrSeparate<["-"], "current_version">, + Visibility<[ClangOption, CC1Option]>; def cxx_isystem : JoinedOrSeparate<["-"], "cxx-isystem">, 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; def fasm : Flag<["-"], "fasm">, Group; +def installapi : Flag<["-"], "installapi">, + Visibility<[ClangOption, CC1Option]>, 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, 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>; def iprefix : JoinedOrSeparate<["-"], "iprefix">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Set the -iwithprefix/-iwithprefixbefore prefix">, MetaVarName<"">; diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def index f72c27e1ee7019..570a53441d1c74 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::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/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 #include @@ -111,6 +112,9 @@ class CompilerInvocationBase { /// Options controlling preprocessed output. std::shared_ptr PreprocessorOutputOpts; + /// Options controlling InstallAPI operations and output. + std::shared_ptr 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 CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + +public: + static std::unique_ptr + CreateOutputFile(CompilerInstance &CI, StringRef InFile); +}; + class GenerateInterfaceStubsAction : public ASTFrontendAction { protected: std::unique_ptr 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 OS = nullptr; + + /// DiagnosticsEngine to report errors. + llvm::IntrusiveRefCntPtr 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..cf84ef21dfa8ce 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(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,13 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, if (!MergerInputs.empty()) Actions.push_back( C.MakeAction(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(*InputArg, InputType); + Actions.push_back( + C.MakeAction(Current, types::TY_TextAPI)); } for (auto Opt : {options::OPT_print_supported_cpus, @@ -4762,6 +4774,8 @@ Action *Driver::ConstructPhaseAction( return C.MakeAction(Input, types::TY_Nothing); if (Args.hasArg(options::OPT_extract_api)) return C.MakeAction(Input, types::TY_API_INFO); + if (Args.hasArg(options::OPT_installapi)) + return C.MakeAction(Input, types::TY_TextAPI); return C.MakeAction(Input, types::TY_LLVM_BC); } case phases::Backend: { @@ -6441,7 +6455,7 @@ bool Driver::ShouldUseClangCompiler(const JobAction &JA) const { // And say "no" if this is not a kind of action clang understands. if (!isa(JA) && !isa(JA) && !isa(JA) && !isa(JA) && - !isa(JA)) + !isa(JA) && !isa(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(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(JA) || isa(JA)) && "Invalid action for clang tool."); diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index a9166672088459..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 @@ -27,6 +28,7 @@ add_clang_library(clangFrontend HeaderIncludeGen.cpp InitPreprocessor.cpp LayoutOverrideSource.cpp + InstallAPIConsumer.cpp LogDiagnosticPrinter.cpp ModuleDependencyCollector.cpp MultiplexConsumer.cpp @@ -53,6 +55,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()), FrontendOpts(std::make_shared()), DependencyOutputOpts(std::make_shared()), - PreprocessorOutputOpts(std::make_shared()) {} + PreprocessorOutputOpts(std::make_shared()), + InstallAPIOpts(std::make_shared()) {} 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 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 +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(std::move(Ctx)); +} + +std::unique_ptr +InstallAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { + std::unique_ptr 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(); case InitOnly: return std::make_unique(); + case InstallAPI: + return std::make_unique(); case ParseSyntaxOnly: return std::make_unique(); case ModuleFileInfo: return std::make_unique(); case VerifyPCH: return std::make_unique(); 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..99379b44d1379c --- /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: 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 +// 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..f93b5d9c945886 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -97,6 +97,7 @@ "llvm-lto", "llvm-lto2", "llvm-profdata", + "llvm-readtapi", ToolSubst( "%clang_extdef_map", command=FindTool("clang-extdef-mapping"),