Skip to content

Commit 726a8d5

Browse files
Merge pull request #35690 from cachemeifyoucan/swift-api-extract-json
swift-api-extract to generate JSON API information
2 parents de63a23 + 02c4165 commit 726a8d5

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)