Skip to content

Commit 09e9895

Browse files
authored
[clang][InstallAPI] Introduce basic driver to write out tbd files (llvm#81571)
This introduces a basic outline of installapi as a clang driver option. It captures relevant information as cc1 args, which are common 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 directly in Clang. This patch does not handle any AST traversing on input yet. InstallAPI is broadly an operation that 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 is the linkable object in all Apple SDKs and when building dylibs in Xcode. `clang -installapi` also will support verification where it compares all the information recorded for the TBD files against the already built binary, to catch possible mismatches like when a declaration is missing a definition for an exported symbol.
1 parent 14b0d0d commit 09e9895

25 files changed

+357
-5
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,4 +804,7 @@ def warn_android_unversioned_fallback : Warning<
804804

805805
def err_drv_triple_version_invalid : Error<
806806
"version '%0' in target triple '%1' is invalid">;
807+
808+
def err_drv_installapi_unsupported : Error<
809+
"InstallAPI is not supported for '%0'">;
807810
}

clang/include/clang/Driver/Action.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class Action {
5959
PreprocessJobClass,
6060
PrecompileJobClass,
6161
ExtractAPIJobClass,
62+
InstallAPIJobClass,
6263
AnalyzeJobClass,
6364
MigrateJobClass,
6465
CompileJobClass,
@@ -448,6 +449,17 @@ class ExtractAPIJobAction : public JobAction {
448449
void addHeaderInput(Action *Input) { getInputs().push_back(Input); }
449450
};
450451

452+
class InstallAPIJobAction : public JobAction {
453+
void anchor() override;
454+
455+
public:
456+
InstallAPIJobAction(Action *Input, types::ID OutputType);
457+
458+
static bool classof(const Action *A) {
459+
return A->getKind() == InstallAPIJobClass;
460+
}
461+
};
462+
451463
class AnalyzeJobAction : public JobAction {
452464
void anchor() override;
453465

clang/include/clang/Driver/Options.td

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ class AnalyzerOpts<string base>
309309
: KeyPathAndMacro<"AnalyzerOpts->", base, "ANALYZER_"> {}
310310
class MigratorOpts<string base>
311311
: KeyPathAndMacro<"MigratorOpts.", base, "MIGRATOR_"> {}
312+
class InstallAPIOpts<string base>
313+
: KeyPathAndMacro<"InstallAPIOpts.", base, "INSTALLAPI_"> {}
312314

313315
// A boolean option which is opt-in in CC1. The positive option exists in CC1 and
314316
// 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=">,
11141116
def coverage : Flag<["-", "--"], "coverage">, Group<Link_Group>,
11151117
Visibility<[ClangOption, CLOption]>;
11161118
def cpp_precomp : Flag<["-"], "cpp-precomp">, Group<clang_ignored_f_Group>;
1117-
def current__version : JoinedOrSeparate<["-"], "current_version">;
1119+
def current__version : JoinedOrSeparate<["-"], "current_version">,
1120+
Visibility<[ClangOption, CC1Option]>;
11181121
def cxx_isystem : JoinedOrSeparate<["-"], "cxx-isystem">, Group<clang_i_Group>,
11191122
HelpText<"Add directory to the C++ SYSTEM include search path">,
11201123
Visibility<[ClangOption, CC1Option]>,
@@ -1529,6 +1532,9 @@ def static_libsan : Flag<["-"], "static-libsan">,
15291532
HelpText<"Statically link the sanitizer runtime (Not supported for ASan, TSan or UBSan on darwin)">;
15301533
def : Flag<["-"], "shared-libasan">, Alias<shared_libsan>;
15311534
def fasm : Flag<["-"], "fasm">, Group<f_Group>;
1535+
def installapi : Flag<["-"], "installapi">,
1536+
Visibility<[ClangOption, CC1Option]>, Group<Action_Group>,
1537+
HelpText<"Create a text-based stub file by scanning header files">;
15321538

15331539
defm assume_unique_vtables : BoolFOption<"assume-unique-vtables",
15341540
CodeGenOpts<"AssumeUniqueVTables">, DefaultTrue,
@@ -4291,7 +4297,9 @@ def verify_pch : Flag<["-"], "verify-pch">, Group<Action_Group>,
42914297
Visibility<[ClangOption, CC1Option]>,
42924298
HelpText<"Load and verify that a pre-compiled header file is not stale">;
42934299
def init : Separate<["-"], "init">;
4294-
def install__name : Separate<["-"], "install_name">;
4300+
def install__name : Separate<["-"], "install_name">,
4301+
Visibility<[ClangOption, CC1Option]>,
4302+
MarshallingInfoString<InstallAPIOpts<"InstallName">>;
42954303
def iprefix : JoinedOrSeparate<["-"], "iprefix">, Group<clang_i_Group>,
42964304
Visibility<[ClangOption, CC1Option]>,
42974305
HelpText<"Set the -iwithprefix/-iwithprefixbefore prefix">, MetaVarName<"<dir>">;

clang/include/clang/Driver/Types.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ TYPE("lto-bc", LTO_BC, INVALID, "o", phases
9494
TYPE("ast", AST, INVALID, "ast", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
9595
TYPE("ifs", IFS, INVALID, "ifs", phases::IfsMerge)
9696
TYPE("ifs-cpp", IFS_CPP, INVALID, "ifs", phases::Compile, phases::IfsMerge)
97+
TYPE("tbd", TextAPI, INVALID, "tbd", phases::Precompile)
9798
TYPE("pcm", ModuleFile, INVALID, "pcm", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
9899
TYPE("header-unit", HeaderUnit, INVALID, "pcm", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
99100
TYPE("plist", Plist, INVALID, "plist", phases::Compile, phases::Backend, phases::Assemble, phases::Link)

clang/include/clang/Frontend/CompilerInstance.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ class CompilerInstance : public ModuleLoader {
294294
return Invocation->getFrontendOpts();
295295
}
296296

297+
InstallAPIOptions &getInstallAPIOpts() {
298+
return Invocation->getInstallAPIOpts();
299+
}
300+
const InstallAPIOptions &getInstallAPIOpts() const {
301+
return Invocation->getInstallAPIOpts();
302+
}
303+
297304
HeaderSearchOptions &getHeaderSearchOpts() {
298305
return Invocation->getHeaderSearchOpts();
299306
}

clang/include/clang/Frontend/CompilerInvocation.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
#include "clang/Basic/LangStandard.h"
1919
#include "clang/Frontend/DependencyOutputOptions.h"
2020
#include "clang/Frontend/FrontendOptions.h"
21+
#include "clang/Frontend/InstallAPIOptions.h"
2122
#include "clang/Frontend/MigratorOptions.h"
2223
#include "clang/Frontend/PreprocessorOutputOptions.h"
2324
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
24-
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2525
#include "llvm/ADT/ArrayRef.h"
26+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2627
#include <memory>
2728
#include <string>
2829

@@ -111,6 +112,9 @@ class CompilerInvocationBase {
111112
/// Options controlling preprocessed output.
112113
std::shared_ptr<PreprocessorOutputOptions> PreprocessorOutputOpts;
113114

115+
/// Options controlling InstallAPI operations and output.
116+
std::shared_ptr<InstallAPIOptions> InstallAPIOpts;
117+
114118
/// Dummy tag type whose instance can be passed into the constructor to
115119
/// prevent creation of the reference-counted option objects.
116120
struct EmptyConstructor {};
@@ -145,6 +149,7 @@ class CompilerInvocationBase {
145149
const PreprocessorOutputOptions &getPreprocessorOutputOpts() const {
146150
return *PreprocessorOutputOpts;
147151
}
152+
const InstallAPIOptions &getInstallAPIOpts() const { return *InstallAPIOpts; }
148153
/// @}
149154

150155
/// Command line generation.
@@ -237,6 +242,7 @@ class CompilerInvocation : public CompilerInvocationBase {
237242
using CompilerInvocationBase::getFrontendOpts;
238243
using CompilerInvocationBase::getDependencyOutputOpts;
239244
using CompilerInvocationBase::getPreprocessorOutputOpts;
245+
using CompilerInvocationBase::getInstallAPIOpts;
240246
/// @}
241247

242248
/// Mutable getters.
@@ -258,6 +264,7 @@ class CompilerInvocation : public CompilerInvocationBase {
258264
PreprocessorOutputOptions &getPreprocessorOutputOpts() {
259265
return *PreprocessorOutputOpts;
260266
}
267+
InstallAPIOptions &getInstallAPIOpts() { return *InstallAPIOpts; }
261268
/// @}
262269

263270
/// Base class internals.

clang/include/clang/Frontend/FrontendActions.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ class GenerateModuleAction : public ASTFrontendAction {
130130
bool shouldEraseOutputFiles() override;
131131
};
132132

133+
class InstallAPIAction : public ASTFrontendAction {
134+
protected:
135+
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
136+
StringRef InFile) override;
137+
138+
public:
139+
static std::unique_ptr<llvm::raw_pwrite_stream>
140+
CreateOutputFile(CompilerInstance &CI, StringRef InFile);
141+
};
142+
133143
class GenerateInterfaceStubsAction : public ASTFrontendAction {
134144
protected:
135145
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,

clang/include/clang/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ enum ActionKind {
100100
/// Only execute frontend initialization.
101101
InitOnly,
102102

103+
// Create TextAPI stub.
104+
InstallAPI,
105+
103106
/// Dump information about a module file.
104107
ModuleFileInfo,
105108

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===--- InstallAPIOptions.h ------------------------------------*- C++ -*-===//
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+
#ifndef LLVM_CLANG_FRONTEND_INSTALLAPIOPTIONS_H
10+
#define LLVM_CLANG_FRONTEND_INSTALLAPIOPTIONS_H
11+
12+
#include "llvm/TextAPI/PackedVersion.h"
13+
14+
namespace clang {
15+
16+
/// InstallAPIOptions - Options for controlling InstallAPI verification and
17+
/// TextAPI output.
18+
class InstallAPIOptions {
19+
public:
20+
/// The install name which is apart of the library's ID.
21+
std::string InstallName;
22+
23+
/// The current version which is apart of the library's ID.
24+
llvm::MachO::PackedVersion CurrentVersion;
25+
};
26+
} // namespace clang
27+
28+
#endif
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===- InstallAPI/Context.h -------------------------------------*- C++ -*-===//
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+
// Top level types for interacting with the generic clang driver and frontend
10+
// for InstallAPI operations.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_CLANG_INSTALLAPI_CONTEXT_H
15+
#define LLVM_CLANG_INSTALLAPI_CONTEXT_H
16+
17+
#include "clang/AST/ASTConsumer.h"
18+
#include "clang/Basic/Diagnostic.h"
19+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
20+
#include "llvm/TextAPI/InterfaceFile.h"
21+
#include "llvm/TextAPI/RecordVisitor.h"
22+
#include "llvm/TextAPI/RecordsSlice.h"
23+
24+
namespace clang {
25+
namespace installapi {
26+
27+
/// Struct used for generating validating InstallAPI.
28+
/// The attributes captured represent all necessary information
29+
/// to generate TextAPI output.
30+
struct InstallAPIContext {
31+
32+
/// Library attributes that are typically passed as linker inputs.
33+
llvm::MachO::RecordsSlice::BinaryAttrs BA;
34+
35+
/// Active target triple to parse.
36+
llvm::Triple TargetTriple{};
37+
38+
/// Output stream to write TextAPI file to.
39+
std::unique_ptr<llvm::raw_pwrite_stream> OS = nullptr;
40+
41+
/// DiagnosticsEngine to report errors.
42+
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags = nullptr;
43+
44+
/// File Path of output location.
45+
StringRef OutputLoc{};
46+
47+
/// What encoding to write output as.
48+
llvm::MachO::FileType FT = llvm::MachO::FileType::TBD_V5;
49+
};
50+
51+
class InstallAPIConsumer : public ASTConsumer {
52+
public:
53+
InstallAPIConsumer(InstallAPIContext InstallAPICtx)
54+
: Ctx(std::move(InstallAPICtx)) {}
55+
56+
void HandleTranslationUnit(ASTContext &ASTContext) override;
57+
58+
private:
59+
InstallAPIContext Ctx;
60+
};
61+
62+
} // namespace installapi
63+
} // namespace clang
64+
65+
#endif // LLVM_CLANG_INSTALLAPI_CONTEXT_H

clang/lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_subdirectory(Tooling)
2323
add_subdirectory(DirectoryWatcher)
2424
add_subdirectory(Index)
2525
add_subdirectory(IndexSerialization)
26+
add_subdirectory(InstallAPI)
2627
add_subdirectory(StaticAnalyzer)
2728
add_subdirectory(Format)
2829
if(CLANG_INCLUDE_TESTS)

clang/lib/Driver/Action.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const char *Action::getClassName(ActionClass AC) {
3232
case CompileJobClass: return "compiler";
3333
case BackendJobClass: return "backend";
3434
case AssembleJobClass: return "assembler";
35+
case InstallAPIJobClass:
36+
return "installapi";
3537
case IfsMergeJobClass: return "interface-stub-merger";
3638
case LinkJobClass: return "linker";
3739
case LipoJobClass: return "lipo";
@@ -362,6 +364,11 @@ void ExtractAPIJobAction::anchor() {}
362364
ExtractAPIJobAction::ExtractAPIJobAction(Action *Inputs, types::ID OutputType)
363365
: JobAction(ExtractAPIJobClass, Inputs, OutputType) {}
364366

367+
void InstallAPIJobAction::anchor() {}
368+
369+
InstallAPIJobAction::InstallAPIJobAction(Action *Inputs, types::ID OutputType)
370+
: JobAction(InstallAPIJobClass, Inputs, OutputType) {}
371+
365372
void AnalyzeJobAction::anchor() {}
366373

367374
AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType)

clang/lib/Driver/Driver.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4189,6 +4189,11 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
41894189
break;
41904190
}
41914191

4192+
if (auto *IAA = dyn_cast<InstallAPIJobAction>(Current)) {
4193+
Current = nullptr;
4194+
break;
4195+
}
4196+
41924197
// FIXME: Should we include any prior module file outputs as inputs of
41934198
// later actions in the same command line?
41944199

@@ -4319,6 +4324,13 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
43194324
if (!MergerInputs.empty())
43204325
Actions.push_back(
43214326
C.MakeAction<IfsMergeJobAction>(MergerInputs, types::TY_Image));
4327+
} else if (Args.hasArg(options::OPT_installapi)) {
4328+
// TODO: Lift restriction once operation can handle multiple inputs.
4329+
assert(Inputs.size() == 1 && "InstallAPI action can only handle 1 input");
4330+
const auto [InputType, InputArg] = Inputs.front();
4331+
Action *Current = C.MakeAction<InputAction>(*InputArg, InputType);
4332+
Actions.push_back(
4333+
C.MakeAction<InstallAPIJobAction>(Current, types::TY_TextAPI));
43224334
}
43234335

43244336
for (auto Opt : {options::OPT_print_supported_cpus,
@@ -4762,6 +4774,8 @@ Action *Driver::ConstructPhaseAction(
47624774
return C.MakeAction<VerifyPCHJobAction>(Input, types::TY_Nothing);
47634775
if (Args.hasArg(options::OPT_extract_api))
47644776
return C.MakeAction<ExtractAPIJobAction>(Input, types::TY_API_INFO);
4777+
if (Args.hasArg(options::OPT_installapi))
4778+
return C.MakeAction<InstallAPIJobAction>(Input, types::TY_TextAPI);
47654779
return C.MakeAction<CompileJobAction>(Input, types::TY_LLVM_BC);
47664780
}
47674781
case phases::Backend: {
@@ -6441,7 +6455,7 @@ bool Driver::ShouldUseClangCompiler(const JobAction &JA) const {
64416455
// And say "no" if this is not a kind of action clang understands.
64426456
if (!isa<PreprocessJobAction>(JA) && !isa<PrecompileJobAction>(JA) &&
64436457
!isa<CompileJobAction>(JA) && !isa<BackendJobAction>(JA) &&
6444-
!isa<ExtractAPIJobAction>(JA))
6458+
!isa<ExtractAPIJobAction>(JA) && !isa<InstallAPIJobAction>(JA))
64456459
return false;
64466460

64476461
return true;

clang/lib/Driver/ToolChain.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
532532
case Action::PrecompileJobClass:
533533
case Action::PreprocessJobClass:
534534
case Action::ExtractAPIJobClass:
535+
case Action::InstallAPIJobClass:
535536
case Action::AnalyzeJobClass:
536537
case Action::MigrateJobClass:
537538
case Action::VerifyPCHJobClass:

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4939,6 +4939,17 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
49394939
if (Arg *ExtractAPIIgnoresFileArg =
49404940
Args.getLastArg(options::OPT_extract_api_ignores_EQ))
49414941
ExtractAPIIgnoresFileArg->render(Args, CmdArgs);
4942+
} else if (isa<InstallAPIJobAction>(JA)) {
4943+
if (!Triple.isOSDarwin())
4944+
D.Diag(diag::err_drv_installapi_unsupported) << Triple.str();
4945+
4946+
CmdArgs.push_back("-installapi");
4947+
// Add necessary library arguments for InstallAPI.
4948+
if (const Arg *A = Args.getLastArg(options::OPT_install__name))
4949+
A->render(Args, CmdArgs);
4950+
if (const Arg *A = Args.getLastArg(options::OPT_current__version))
4951+
A->render(Args, CmdArgs);
4952+
49424953
} else {
49434954
assert((isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) &&
49444955
"Invalid action for clang tool.");

clang/lib/Frontend/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
77
ProfileData
88
Support
99
TargetParser
10+
TextAPI
1011
)
1112

1213
add_clang_library(clangFrontend
@@ -27,6 +28,7 @@ add_clang_library(clangFrontend
2728
HeaderIncludeGen.cpp
2829
InitPreprocessor.cpp
2930
LayoutOverrideSource.cpp
31+
InstallAPIConsumer.cpp
3032
LogDiagnosticPrinter.cpp
3133
ModuleDependencyCollector.cpp
3234
MultiplexConsumer.cpp
@@ -53,6 +55,7 @@ add_clang_library(clangFrontend
5355
clangBasic
5456
clangDriver
5557
clangEdit
58+
clangInstallAPI
5659
clangLex
5760
clangParse
5861
clangSema

0 commit comments

Comments
 (0)