Skip to content

Commit 089c613

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 0b45124 commit 089c613

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
@@ -138,6 +138,7 @@ WARNING(emit_swift_ranges_without_primary_file,none,
138138
WARNING(emit_compiled_source_without_primary_file,none,
139139
"ignoring -emit-compiled-source (requires -primary-file)", ())
140140

141+
ERROR(error_module_name_required,none, "-module-name is required", ())
141142
ERROR(error_bad_module_name,none,
142143
"module name \"%0\" is not a valid identifier"
143144
"%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

@@ -195,7 +198,8 @@ def driver_mode : Joined<["--"], "driver-mode=">, Flags<[HelpHidden]>,
195198
HelpText<"Set the driver mode to either 'swift' or 'swiftc'">;
196199

197200
def help : Flag<["-", "--"], "help">,
198-
Flags<[FrontendOption, AutolinkExtractOption, ModuleWrapOption, SwiftIndentOption]>,
201+
Flags<[FrontendOption, AutolinkExtractOption, ModuleWrapOption,
202+
SwiftIndentOption, SwiftAPIExtractOption]>,
199203
HelpText<"Display available options">;
200204
def h : Flag<["-"], "h">, Alias<help>;
201205
def help_hidden : Flag<["-", "--"], "help-hidden">,
@@ -217,16 +221,17 @@ def _DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>,
217221

218222
def o : JoinedOrSeparate<["-"], "o">,
219223
Flags<[FrontendOption, AutolinkExtractOption, ModuleWrapOption,
220-
NoInteractiveOption, SwiftIndentOption, ArgumentIsPath]>,
224+
NoInteractiveOption, SwiftIndentOption, ArgumentIsPath,
225+
SwiftAPIExtractOption]>,
221226
HelpText<"Write output to <file>">, MetaVarName<"<file>">;
222227

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

226-
def sdk : Separate<["-"], "sdk">, Flags<[FrontendOption, ArgumentIsPath]>,
231+
def sdk : Separate<["-"], "sdk">, Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
227232
HelpText<"Compile against <sdk>">, MetaVarName<"<sdk>">;
228233

229-
def swift_version : Separate<["-"], "swift-version">, Flags<[FrontendOption, ModuleInterfaceOption]>,
234+
def swift_version : Separate<["-"], "swift-version">, Flags<[FrontendOption, ModuleInterfaceOption, SwiftAPIExtractOption]>,
230235
HelpText<"Interpret input according to a specific Swift language version number">,
231236
MetaVarName<"<vers>">;
232237

@@ -243,16 +248,16 @@ def tools_directory : Separate<["-"], "tools-directory">,
243248
def D : JoinedOrSeparate<["-"], "D">, Flags<[FrontendOption]>,
244249
HelpText<"Marks a conditional compilation flag as true">;
245250

246-
def F : JoinedOrSeparate<["-"], "F">, Flags<[FrontendOption, ArgumentIsPath]>,
251+
def F : JoinedOrSeparate<["-"], "F">, Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
247252
HelpText<"Add directory to framework search path">;
248253
def F_EQ : Joined<["-"], "F=">, Flags<[FrontendOption, ArgumentIsPath]>,
249254
Alias<F>;
250255

251256
def Fsystem : Separate<["-"], "Fsystem">,
252-
Flags<[FrontendOption, ArgumentIsPath]>,
257+
Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
253258
HelpText<"Add directory to system framework search path">;
254259

255-
def I : JoinedOrSeparate<["-"], "I">, Flags<[FrontendOption, ArgumentIsPath]>,
260+
def I : JoinedOrSeparate<["-"], "I">, Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
256261
HelpText<"Add directory to the import search path">;
257262
def I_EQ : Joined<["-"], "I=">, Flags<[FrontendOption, ArgumentIsPath]>,
258263
Alias<I>;
@@ -398,7 +403,7 @@ def localization_path : Separate<["-"], "localization-path">,
398403
MetaVarName<"<path>">;
399404

400405
def module_cache_path : Separate<["-"], "module-cache-path">,
401-
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
406+
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath, SwiftAPIExtractOption]>,
402407
HelpText<"Specifies the Clang module cache path">;
403408

404409
def enable_library_evolution : Flag<["-"], "enable-library-evolution">,
@@ -419,7 +424,7 @@ def define_availability : Separate<["-"], "define-availability">,
419424
MetaVarName<"<macro>">;
420425

421426
def module_name : Separate<["-"], "module-name">,
422-
Flags<[FrontendOption, ModuleInterfaceOption]>,
427+
Flags<[FrontendOption, ModuleInterfaceOption, SwiftAPIExtractOption]>,
423428
HelpText<"Name of the module to build">;
424429
def module_name_EQ : Joined<["-"], "module-name=">, Flags<[FrontendOption]>,
425430
Alias<module_name>;
@@ -682,7 +687,7 @@ def framework : Separate<["-"], "framework">, Group<linker_option_Group>,
682687
HelpText<"Specifies a framework which should be linked against">;
683688

684689
def L : JoinedOrSeparate<["-"], "L">, Group<linker_option_Group>,
685-
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
690+
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath, SwiftAPIExtractOption]>,
686691
HelpText<"Add directory to library link search path">;
687692
def L_EQ : Joined<["-"], "L=">, Group<linker_option_Group>,
688693
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
@@ -1046,7 +1051,7 @@ def resource_dir : Separate<["-"], "resource-dir">,
10461051
HelpText<"The directory that holds the compiler resource files">;
10471052

10481053
def target : Separate<["-"], "target">,
1049-
Flags<[FrontendOption, ModuleWrapOption, ModuleInterfaceOption]>,
1054+
Flags<[FrontendOption, ModuleWrapOption, ModuleInterfaceOption, SwiftAPIExtractOption]>,
10501055
HelpText<"Generate code for the given target <triple>, such as x86_64-apple-macos10.9">, MetaVarName<"<triple>">;
10511056
def target_legacy_spelling : Joined<["--"], "target=">,
10521057
Flags<[FrontendOption]>, Alias<target>;
@@ -1162,7 +1167,7 @@ def working_directory_EQ : Joined<["-"], "working-directory=">,
11621167
// VFS
11631168

11641169
def vfsoverlay : JoinedOrSeparate<["-"], "vfsoverlay">,
1165-
Flags<[FrontendOption, ArgumentIsPath]>,
1170+
Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption]>,
11661171
HelpText<"Add directory to VFS overlay file">;
11671172
def vfsoverlay_EQ : Joined<["-"], "vfsoverlay=">,
11681173
Alias<vfsoverlay>;
@@ -1182,4 +1187,8 @@ def disable_autolinking_runtime_compatibility_dynamic_replacements
11821187
HelpText<"Do not use autolinking for the dynamic replacement runtime "
11831188
"compatibility library">;
11841189

1190+
// Swift API Extraction only options
1191+
def pretty_print: Flag<["-"], "pretty-print">, Flags<[SwiftAPIExtractOption]>,
1192+
HelpText<"Pretty-print the output JSON">;
1193+
11851194
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())
@@ -3493,6 +3494,7 @@ void Driver::printHelp(bool ShowHidden) const {
34933494
case DriverKind::AutolinkExtract:
34943495
case DriverKind::SwiftIndent:
34953496
case DriverKind::SymbolGraph:
3497+
case DriverKind::APIExtract:
34963498
ExcludedFlagsBitmask |= options::NoBatchOption;
34973499
break;
34983500
}

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)