Skip to content

Commit 02c4165

Browse files
swift-api-extract to generate JSON API information
Add a new swift-frontend driver option that extract APIs in the swift module and print in JSON format. This is to allow tooling to understand and process swift APIs without the need to be a swift compiler or understand swift module/AST.
1 parent 77d828d commit 02c4165

23 files changed

+1583
-34
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ ERROR(cannot_emit_ir_skipping_function_bodies,none,
132132
WARNING(emit_reference_dependencies_without_primary_file,none,
133133
"ignoring -emit-reference-dependencies (requires -primary-file)", ())
134134

135+
ERROR(error_module_name_required,none, "-module-name is required", ())
135136
ERROR(error_bad_module_name,none,
136137
"module name \"%0\" is not a valid identifier"
137138
"%select{|; use -module-name flag to specify an alternate name}1",

include/swift/AST/TBDGenRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ namespace swift {
4141
class FileUnit;
4242
class ModuleDecl;
4343

44+
namespace apigen {
45+
class API;
46+
} // end namespace apigen
47+
4448
class TBDGenDescriptor final {
4549
using FileOrModule = llvm::PointerUnion<FileUnit *, ModuleDecl *>;
4650
FileOrModule Input;
@@ -120,6 +124,21 @@ class PublicSymbolsRequest
120124
evaluate(Evaluator &evaluator, TBDGenDescriptor desc) const;
121125
};
122126

127+
/// Retrieve API information for a file or module.
128+
class APIGenRequest
129+
: public SimpleRequest<APIGenRequest,
130+
apigen::API(TBDGenDescriptor),
131+
RequestFlags::Uncached> {
132+
public:
133+
using SimpleRequest::SimpleRequest;
134+
135+
private:
136+
friend SimpleRequest;
137+
138+
// Evaluation.
139+
apigen::API evaluate(Evaluator &evaluator, TBDGenDescriptor desc) const;
140+
};
141+
123142
/// Describes the origin of a particular symbol, including the stage of
124143
/// compilation it is introduced, as well as information on what decl introduces
125144
/// it.

include/swift/AST/TBDGenTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ SWIFT_REQUEST(TBDGen, PublicSymbolsRequest,
2222
SWIFT_REQUEST(TBDGen, SymbolSourceMapRequest,
2323
SymbolSourceMap(TBDGenDescriptor),
2424
Cached, NoLocationInfo)
25+
SWIFT_REQUEST(TBDGen, APIGenRequest, apigen::API(TBDGenDescriptor), Uncached,
26+
NoLocationInfo)

include/swift/Driver/Driver.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ class Driver {
166166
Batch, // swiftc
167167
AutolinkExtract, // swift-autolink-extract
168168
SwiftIndent, // swift-indent
169-
SymbolGraph // swift-symbolgraph
169+
SymbolGraph, // swift-symbolgraph
170+
APIExtract // swift-api-extract
170171
};
171172

172173
class InputInfoMap;

include/swift/Option/Options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace options {
3737
ArgumentIsPath = (1 << 12),
3838
ModuleInterfaceOption = (1 << 13),
3939
SupplementaryOutput = (1 << 14),
40+
SwiftAPIExtractOption = (1 << 15),
4041
};
4142

4243
enum ID {

include/swift/Option/Options.td

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ def ModuleInterfaceOption : OptionFlag;
5555
// for a supplementary output. E.g., `-emit-module` and `-emit-module-path`.
5656
def SupplementaryOutput : OptionFlag;
5757

58+
// The option should be accepted by swift-api-extract.
59+
def SwiftAPIExtractOption : OptionFlag;
60+
5861
/////////
5962
// Options
6063

@@ -169,7 +172,8 @@ def driver_mode : Joined<["--"], "driver-mode=">, Flags<[HelpHidden]>,
169172
HelpText<"Set the driver mode to either 'swift' or 'swiftc'">;
170173

171174
def help : Flag<["-", "--"], "help">,
172-
Flags<[FrontendOption, AutolinkExtractOption, ModuleWrapOption, SwiftIndentOption]>,
175+
Flags<[FrontendOption, AutolinkExtractOption, ModuleWrapOption,
176+
SwiftIndentOption, SwiftAPIExtractOption]>,
173177
HelpText<"Display available options">;
174178
def h : Flag<["-"], "h">, Alias<help>;
175179
def help_hidden : Flag<["-", "--"], "help-hidden">,
@@ -191,16 +195,17 @@ def _DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>,
191195

192196
def o : JoinedOrSeparate<["-"], "o">,
193197
Flags<[FrontendOption, AutolinkExtractOption, ModuleWrapOption,
194-
NoInteractiveOption, SwiftIndentOption, ArgumentIsPath]>,
198+
NoInteractiveOption, SwiftIndentOption, ArgumentIsPath,
199+
SwiftAPIExtractOption]>,
195200
HelpText<"Write output to <file>">, MetaVarName<"<file>">;
196201

197202
def j : JoinedOrSeparate<["-"], "j">, Flags<[DoesNotAffectIncrementalBuild]>,
198203
HelpText<"Number of commands to execute in parallel">, MetaVarName<"<n>">;
199204

200-
def sdk : Separate<["-"], "sdk">, Flags<[FrontendOption, ArgumentIsPath]>,
205+
def sdk : Separate<["-"], "sdk">, Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
201206
HelpText<"Compile against <sdk>">, MetaVarName<"<sdk>">;
202207

203-
def swift_version : Separate<["-"], "swift-version">, Flags<[FrontendOption, ModuleInterfaceOption]>,
208+
def swift_version : Separate<["-"], "swift-version">, Flags<[FrontendOption, ModuleInterfaceOption, SwiftAPIExtractOption]>,
204209
HelpText<"Interpret input according to a specific Swift language version number">,
205210
MetaVarName<"<vers>">;
206211

@@ -217,16 +222,16 @@ def tools_directory : Separate<["-"], "tools-directory">,
217222
def D : JoinedOrSeparate<["-"], "D">, Flags<[FrontendOption]>,
218223
HelpText<"Marks a conditional compilation flag as true">;
219224

220-
def F : JoinedOrSeparate<["-"], "F">, Flags<[FrontendOption, ArgumentIsPath]>,
225+
def F : JoinedOrSeparate<["-"], "F">, Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
221226
HelpText<"Add directory to framework search path">;
222227
def F_EQ : Joined<["-"], "F=">, Flags<[FrontendOption, ArgumentIsPath]>,
223228
Alias<F>;
224229

225230
def Fsystem : Separate<["-"], "Fsystem">,
226-
Flags<[FrontendOption, ArgumentIsPath]>,
231+
Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
227232
HelpText<"Add directory to system framework search path">;
228233

229-
def I : JoinedOrSeparate<["-"], "I">, Flags<[FrontendOption, ArgumentIsPath]>,
234+
def I : JoinedOrSeparate<["-"], "I">, Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
230235
HelpText<"Add directory to the import search path">;
231236
def I_EQ : Joined<["-"], "I=">, Flags<[FrontendOption, ArgumentIsPath]>,
232237
Alias<I>;
@@ -372,7 +377,7 @@ def localization_path : Separate<["-"], "localization-path">,
372377
MetaVarName<"<path>">;
373378

374379
def module_cache_path : Separate<["-"], "module-cache-path">,
375-
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
380+
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath, SwiftAPIExtractOption]>,
376381
HelpText<"Specifies the Clang module cache path">;
377382

378383
def enable_library_evolution : Flag<["-"], "enable-library-evolution">,
@@ -393,7 +398,7 @@ def define_availability : Separate<["-"], "define-availability">,
393398
MetaVarName<"<macro>">;
394399

395400
def module_name : Separate<["-"], "module-name">,
396-
Flags<[FrontendOption, ModuleInterfaceOption]>,
401+
Flags<[FrontendOption, ModuleInterfaceOption, SwiftAPIExtractOption]>,
397402
HelpText<"Name of the module to build">;
398403
def module_name_EQ : Joined<["-"], "module-name=">, Flags<[FrontendOption]>,
399404
Alias<module_name>;
@@ -663,7 +668,7 @@ def framework : Separate<["-"], "framework">, Group<linker_option_Group>,
663668
HelpText<"Specifies a framework which should be linked against">;
664669

665670
def L : JoinedOrSeparate<["-"], "L">, Group<linker_option_Group>,
666-
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
671+
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath, SwiftAPIExtractOption]>,
667672
HelpText<"Add directory to library link search path">;
668673
def L_EQ : Joined<["-"], "L=">, Group<linker_option_Group>,
669674
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
@@ -1027,7 +1032,7 @@ def resource_dir : Separate<["-"], "resource-dir">,
10271032
HelpText<"The directory that holds the compiler resource files">;
10281033

10291034
def target : Separate<["-"], "target">,
1030-
Flags<[FrontendOption, ModuleWrapOption, ModuleInterfaceOption]>,
1035+
Flags<[FrontendOption, ModuleWrapOption, ModuleInterfaceOption, SwiftAPIExtractOption]>,
10311036
HelpText<"Generate code for the given target <triple>, such as x86_64-apple-macos10.9">, MetaVarName<"<triple>">;
10321037
def target_legacy_spelling : Joined<["--"], "target=">,
10331038
Flags<[FrontendOption]>, Alias<target>;
@@ -1146,7 +1151,7 @@ def working_directory_EQ : Joined<["-"], "working-directory=">,
11461151
// VFS
11471152

11481153
def vfsoverlay : JoinedOrSeparate<["-"], "vfsoverlay">,
1149-
Flags<[FrontendOption, ArgumentIsPath]>,
1154+
Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
11501155
HelpText<"Add directory to VFS overlay file">;
11511156
def vfsoverlay_EQ : Joined<["-"], "vfsoverlay=">,
11521157
Alias<vfsoverlay>;
@@ -1177,4 +1182,8 @@ def emit_symbol_graph_dir : Separate<["-"], "emit-symbol-graph-dir">,
11771182
HelpText<"Emit a symbol graph to directory <dir>">,
11781183
MetaVarName<"<dir>">;
11791184

1185+
// Swift API Extraction only options
1186+
def pretty_print: Flag<["-"], "pretty-print">, Flags<[SwiftAPIExtractOption]>,
1187+
HelpText<"Pretty-print the output JSON">;
1188+
11801189
include "FrontendOptions.td"

include/swift/TBDGen/TBDGen.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ std::vector<std::string> getPublicSymbols(TBDGenDescriptor desc);
9999
void writeTBDFile(ModuleDecl *M, llvm::raw_ostream &os,
100100
const TBDGenOptions &opts);
101101

102+
void writeAPIJSONFile(ModuleDecl *M, llvm::raw_ostream &os, bool PrettyPrint);
103+
102104
} // end namespace swift
103105

104106
#endif

lib/Driver/Driver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ void Driver::parseDriverKind(ArrayRef<const char *> Args) {
102102
.Case("swift-autolink-extract", DriverKind::AutolinkExtract)
103103
.Case("swift-indent", DriverKind::SwiftIndent)
104104
.Case("swift-symbolgraph-extract", DriverKind::SymbolGraph)
105+
.Case("swift-api-extract", DriverKind::APIExtract)
105106
.Default(None);
106107

107108
if (Kind.hasValue())
@@ -3467,6 +3468,7 @@ void Driver::printHelp(bool ShowHidden) const {
34673468
case DriverKind::AutolinkExtract:
34683469
case DriverKind::SwiftIndent:
34693470
case DriverKind::SymbolGraph:
3471+
case DriverKind::APIExtract:
34703472
ExcludedFlagsBitmask |= options::NoBatchOption;
34713473
break;
34723474
}

lib/TBDGen/APIGen.cpp

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
//===--- APIGen.cpp - Swift API Generation --------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file implements the entrypoints into API file generation.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
18+
#include "llvm/ADT/StringSwitch.h"
19+
#include "llvm/Support/JSON.h"
20+
#include "llvm/Support/raw_ostream.h"
21+
22+
#include "APIGen.h"
23+
24+
namespace swift {
25+
namespace apigen {
26+
27+
void API::addSymbol(llvm::StringRef symbol, APILoc loc, APILinkage linkage,
28+
APIFlags flags, APIAccess access,
29+
APIAvailability availability) {
30+
globals.emplace_back(symbol, loc, linkage, flags, access, GVKind::Function,
31+
availability);
32+
}
33+
34+
ObjCInterfaceRecord *API::addObjCClass(llvm::StringRef name, APILinkage linkage,
35+
APILoc loc, APIAccess access,
36+
APIAvailability availability,
37+
llvm::StringRef superClassName) {
38+
interfaces.emplace_back(name, linkage, loc, access, availability,
39+
superClassName);
40+
return &interfaces.back();
41+
}
42+
43+
void API::addObjCMethod(ObjCInterfaceRecord *cls, llvm::StringRef name,
44+
APILoc loc, APIAccess access, bool isInstanceMethod,
45+
bool isOptional, APIAvailability availability) {
46+
cls->methods.emplace_back(name, loc, access, isInstanceMethod, isOptional,
47+
availability);
48+
}
49+
50+
static void serialize(llvm::json::OStream &OS, APIAccess access) {
51+
switch (access) {
52+
case APIAccess::Public:
53+
OS.attribute("access", "public");
54+
break;
55+
case APIAccess::Private:
56+
OS.attribute("access", "private");
57+
break;
58+
case APIAccess::Project:
59+
OS.attribute("access", "project");
60+
break;
61+
case APIAccess::Unknown:
62+
break;
63+
}
64+
}
65+
66+
static void serialize(llvm::json::OStream &OS, APIAvailability availability) {
67+
if (availability.empty())
68+
return;
69+
if (!availability.introduced.empty())
70+
OS.attribute("introduced", availability.introduced);
71+
if (!availability.obsoleted.empty())
72+
OS.attribute("obsoleted", availability.obsoleted);
73+
if (availability.unavailable)
74+
OS.attribute("unavailable", availability.unavailable);
75+
}
76+
77+
static void serialize(llvm::json::OStream &OS, APILinkage linkage) {
78+
switch (linkage) {
79+
case APILinkage::Exported:
80+
OS.attribute("linkage", "exported");
81+
break;
82+
case APILinkage::Reexported:
83+
OS.attribute("linkage", "re-exported");
84+
break;
85+
case APILinkage::Internal:
86+
OS.attribute("linkage", "internal");
87+
break;
88+
case APILinkage::External:
89+
OS.attribute("linkage", "external");
90+
break;
91+
case APILinkage::Unknown:
92+
// do nothing;
93+
break;
94+
}
95+
}
96+
97+
static void serialize(llvm::json::OStream &OS, APILoc loc) {
98+
OS.attribute("file", loc.getFilename());
99+
}
100+
101+
static void serialize(llvm::json::OStream &OS, const GlobalRecord &record) {
102+
OS.object([&]() {
103+
OS.attribute("name", record.name);
104+
serialize(OS, record.access);
105+
serialize(OS, record.loc);
106+
serialize(OS, record.linkage);
107+
serialize(OS, record.availability);
108+
});
109+
}
110+
111+
static void serialize(llvm::json::OStream &OS, const ObjCMethodRecord &record) {
112+
OS.object([&]() {
113+
OS.attribute("name", record.name);
114+
serialize(OS, record.access);
115+
serialize(OS, record.loc);
116+
serialize(OS, record.linkage);
117+
serialize(OS, record.availability);
118+
if (record.isOptional)
119+
OS.attribute("optional", record.isOptional);
120+
});
121+
}
122+
123+
static void serialize(llvm::json::OStream &OS,
124+
const ObjCInterfaceRecord &record) {
125+
OS.object([&]() {
126+
OS.attribute("name", record.name);
127+
serialize(OS, record.access);
128+
serialize(OS, record.loc);
129+
serialize(OS, record.linkage);
130+
serialize(OS, record.availability);
131+
OS.attribute("super", record.superClassName);
132+
OS.attributeArray("instanceMethods", [&]() {
133+
for (auto &method : record.methods) {
134+
if (method.isInstanceMethod)
135+
serialize(OS, method);
136+
}
137+
});
138+
OS.attributeArray("classMethods", [&]() {
139+
for (auto &method : record.methods) {
140+
if (!method.isInstanceMethod)
141+
serialize(OS, method);
142+
}
143+
});
144+
});
145+
}
146+
147+
static bool sortAPIRecords(const APIRecord &base, const APIRecord &compare) {
148+
return base.name < compare.name;
149+
}
150+
151+
void API::writeAPIJSONFile(llvm::raw_ostream &os, bool PrettyPrint) {
152+
unsigned indentSize = PrettyPrint ? 2 : 0;
153+
llvm::json::OStream JSON(os, indentSize);
154+
155+
// FIXME: only write PublicSDKContentRoot now.
156+
JSON.object([&]() {
157+
JSON.attribute("target", target.str());
158+
JSON.attributeArray("globals", [&]() {
159+
llvm::sort(globals, sortAPIRecords);
160+
for (const auto &g : globals)
161+
serialize(JSON, g);
162+
});
163+
JSON.attributeArray("interfaces", [&]() {
164+
llvm::sort(interfaces, sortAPIRecords);
165+
for (const auto &i : interfaces)
166+
serialize(JSON, i);
167+
});
168+
JSON.attribute("version", "1.0");
169+
});
170+
}
171+
172+
} // end namespace apigen
173+
} // end namespace swift

0 commit comments

Comments
 (0)