Skip to content

Commit 33cdd61

Browse files
committed
Fast dependency scanning for Swift
Implement a new "fast" dependency scanning option, `-scan-dependencies`, in the Swift frontend that determines all of the source file and module dependencies for a given set of Swift sources. It covers four forms of modules: 1) Swift (serialized) module files, by reading the module header 2) Swift interface files, by parsing the source code to find imports 3) Swift source modules, by parsing the source code to find imports 4) Clang modules, using Clang's fast dependency scanning tool A single `-scan-dependencies` operation maps out the full dependency graph for the given Swift source files, including all of the Swift and Clang modules that may need to be built, such that all of the work can be scheduled up front by the Swift driver or any other build system that understands this option. The dependency graph is emitted as JSON, which can be consumed by these other tools.
1 parent 46703b4 commit 33cdd61

36 files changed

+1454
-10
lines changed

include/swift/AST/ASTContext.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ namespace swift {
7878
class LazyContextData;
7979
class LazyIterableDeclContextData;
8080
class LazyMemberLoader;
81+
class ModuleDependencies;
8182
class PatternBindingDecl;
8283
class PatternBindingInitializer;
8384
class SourceFile;
@@ -91,6 +92,7 @@ namespace swift {
9192
class Identifier;
9293
class InheritedNameSet;
9394
class ModuleDecl;
95+
class ModuleDependenciesCache;
9496
class ModuleLoader;
9597
class NominalTypeDecl;
9698
class NormalProtocolConformance;
@@ -710,6 +712,15 @@ class ASTContext final {
710712
void addModuleLoader(std::unique_ptr<ModuleLoader> loader,
711713
bool isClang = false, bool isDWARF = false);
712714

715+
/// Retrieve the module dependencies for the module with the given name.
716+
///
717+
/// \param isUnderlyingClangModule When true, only look for a Clang module
718+
/// with the given name, ignoring any Swift modules.
719+
Optional<ModuleDependencies> getModuleDependencies(
720+
StringRef moduleName,
721+
bool isUnderlyingClangModule,
722+
ModuleDependenciesCache &cache);
723+
713724
/// Load extensions to the given nominal type from the external
714725
/// module loaders.
715726
///
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
//===--- ModuleDependencies.h - Module Dependencies -------------*- C++ -*-===//
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 defines data structures for capturing module dependencies.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
#ifndef SWIFT_AST_MODULE_DEPENDENCIES_H
17+
#define SWIFT_AST_MODULE_DEPENDENCIES_H
18+
19+
#include "swift/Basic/LLVM.h"
20+
#include "llvm/ADT/ArrayRef.h"
21+
#include "llvm/ADT/DenseMap.h"
22+
#include "llvm/ADT/Optional.h"
23+
#include "llvm/ADT/StringSet.h"
24+
#include <string>
25+
#include <vector>
26+
27+
namespace swift {
28+
29+
class ClangModuleDependenciesCacheImpl;
30+
class SourceFile;
31+
32+
/// Which kind of module dependencies we are looking for.
33+
enum class ModuleDependenciesKind : int8_t {
34+
Swift,
35+
Clang,
36+
};
37+
38+
/// Base class for the variant storage of ModuleDependencies.
39+
class ModuleDependenciesStorageBase {
40+
public:
41+
const bool isSwiftModule;
42+
43+
ModuleDependenciesStorageBase(bool isSwiftModule,
44+
const std::string &compiledModulePath)
45+
: isSwiftModule(isSwiftModule),
46+
compiledModulePath(compiledModulePath) { }
47+
48+
virtual ModuleDependenciesStorageBase *clone() const = 0;
49+
50+
virtual ~ModuleDependenciesStorageBase();
51+
52+
/// The path to the compiled module file.
53+
const std::string compiledModulePath;
54+
55+
/// The set of modules on which this module depends.
56+
std::vector<std::string> moduleDependencies;
57+
};
58+
59+
/// Describes the dependencies of a Swift module.
60+
class SwiftModuleDependenciesStorage : public ModuleDependenciesStorageBase {
61+
public:
62+
/// The Swift interface file, if it can be used to generate the module file.
63+
const Optional<std::string> swiftInterfaceFile;
64+
65+
/// Bridging header file, if there is one.
66+
Optional<std::string> bridgingHeaderFile;
67+
68+
/// Swift source files that are part of the Swift module, when known.
69+
std::vector<std::string> sourceFiles;
70+
71+
/// Source files on which the bridging header depends.
72+
std::vector<std::string> bridgingSourceFiles;
73+
74+
SwiftModuleDependenciesStorage(
75+
const std::string &compiledModulePath,
76+
const Optional<std::string> &swiftInterfaceFile
77+
) : ModuleDependenciesStorageBase(/*isSwiftModule=*/true, compiledModulePath),
78+
swiftInterfaceFile(swiftInterfaceFile) { }
79+
80+
ModuleDependenciesStorageBase *clone() const override {
81+
return new SwiftModuleDependenciesStorage(*this);
82+
}
83+
84+
static bool classof(const ModuleDependenciesStorageBase *base) {
85+
return base->isSwiftModule;
86+
}
87+
};
88+
89+
class ClangModuleDependenciesStorage : public ModuleDependenciesStorageBase {
90+
public:
91+
/// The module map file used to generate the Clang module.
92+
const std::string moduleMapFile;
93+
94+
/// The file dependencies
95+
const std::vector<std::string> fileDependencies;
96+
97+
ClangModuleDependenciesStorage(
98+
const std::string &compiledModulePath,
99+
const std::string &moduleMapFile,
100+
const std::vector<std::string> &fileDependencies
101+
) : ModuleDependenciesStorageBase(/*isSwiftModule=*/false,
102+
compiledModulePath),
103+
moduleMapFile(moduleMapFile),
104+
fileDependencies(fileDependencies) { }
105+
106+
ModuleDependenciesStorageBase *clone() const override {
107+
return new ClangModuleDependenciesStorage(*this);
108+
}
109+
110+
static bool classof(const ModuleDependenciesStorageBase *base) {
111+
return !base->isSwiftModule;
112+
}
113+
};
114+
115+
/// Describes the dependencies of a given module.
116+
class ModuleDependencies {
117+
private:
118+
std::unique_ptr<ModuleDependenciesStorageBase> storage;
119+
120+
ModuleDependencies(std::unique_ptr<ModuleDependenciesStorageBase> &&storage)
121+
: storage(std::move(storage)) { }
122+
123+
public:
124+
ModuleDependencies(const ModuleDependencies &other)
125+
: storage(other.storage->clone()) { }
126+
ModuleDependencies(ModuleDependencies &&other) = default;
127+
128+
ModuleDependencies &operator=(const ModuleDependencies &other) {
129+
storage.reset(other.storage->clone());
130+
return *this;
131+
}
132+
133+
ModuleDependencies &operator=(ModuleDependencies &&other) = default;
134+
135+
/// Describe the module dependencies for a Swift module that can be
136+
/// built from a Swift interface file (\c .swiftinterface).
137+
static ModuleDependencies forSwiftInterface(
138+
const std::string &compiledModulePath,
139+
const std::string &swiftInterfaceFile) {
140+
return ModuleDependencies(
141+
std::make_unique<SwiftModuleDependenciesStorage>(
142+
compiledModulePath, swiftInterfaceFile));
143+
}
144+
145+
/// Describe the module dependencies for a serialized or parsed Swift module.
146+
static ModuleDependencies forSwiftModule(
147+
const std::string &compiledModulePath) {
148+
return ModuleDependencies(
149+
std::make_unique<SwiftModuleDependenciesStorage>(
150+
compiledModulePath, None));
151+
}
152+
153+
/// Describe the module dependencies for a Clang module that can be
154+
/// built from a module map and headers.
155+
static ModuleDependencies forClangModule(
156+
const std::string &compiledModulePath,
157+
const std::string &moduleMapFile,
158+
const std::vector<std::string> &fileDependencies) {
159+
return ModuleDependencies(
160+
std::make_unique<ClangModuleDependenciesStorage>(compiledModulePath,
161+
moduleMapFile,
162+
fileDependencies));
163+
}
164+
165+
/// Retrieve the path to the compiled module.
166+
const std::string getCompiledModulePath() const {
167+
return storage->compiledModulePath;
168+
}
169+
170+
/// Retrieve the module-level dependencies.
171+
ArrayRef<std::string> getModuleDependencies() const {
172+
return storage->moduleDependencies;
173+
}
174+
175+
/// Whether the dependencies are for a Swift module.
176+
bool isSwiftModule() const;
177+
178+
ModuleDependenciesKind getKind() const {
179+
return isSwiftModule() ? ModuleDependenciesKind::Swift
180+
: ModuleDependenciesKind::Clang;
181+
}
182+
/// Retrieve the dependencies for a Swift module.
183+
const SwiftModuleDependenciesStorage *getAsSwiftModule() const;
184+
185+
/// Retrieve the dependencies for a Clang module.
186+
const ClangModuleDependenciesStorage *getAsClangModule() const;
187+
188+
/// Add a dependency on the given module, if it was not already in the set.
189+
void addModuleDependency(StringRef module,
190+
llvm::StringSet<> &alreadyAddedModules);
191+
192+
/// Add all of the module dependencies for the imports in the given source
193+
/// file to the set of module dependencies.
194+
void addModuleDependencies(const SourceFile &sf,
195+
llvm::StringSet<> &alreadyAddedModules);
196+
197+
/// Add a bridging header to a Swift module's dependencies.
198+
void addBridgingHeader(StringRef bridgingHeader);
199+
};
200+
201+
using ModuleDependencyID = std::pair<std::string, ModuleDependenciesKind>;
202+
203+
/// A cache describing the set of module dependencies that has been queried
204+
/// thus far.
205+
class ModuleDependenciesCache {
206+
/// All cached module dependencies, in the order in which they were
207+
/// encountered.
208+
std::vector<ModuleDependencyID> AllModules;
209+
210+
/// Dependencies for Swift modules that have already been computed.
211+
llvm::StringMap<ModuleDependencies> SwiftModuleDependencies;
212+
213+
/// Dependencies for Clang modules that have already been computed.
214+
llvm::StringMap<ModuleDependencies> ClangModuleDependencies;
215+
216+
/// Additional information needed for Clang dependency scanning.
217+
ClangModuleDependenciesCacheImpl *clangImpl = nullptr;
218+
219+
/// Function that will delete \c clangImpl properly.
220+
void (*clangImplDeleter)(ClangModuleDependenciesCacheImpl *) = nullptr;
221+
222+
/// Free up the storage associated with the Clang implementation.
223+
void destroyClangImpl() {
224+
if (this->clangImplDeleter)
225+
this->clangImplDeleter(this->clangImpl);
226+
}
227+
228+
/// Retrieve the dependencies map that corresponds to the given dependency
229+
/// kind.
230+
llvm::StringMap<ModuleDependencies> &getDependenciesMap(
231+
ModuleDependenciesKind kind);
232+
const llvm::StringMap<ModuleDependencies> &getDependenciesMap(
233+
ModuleDependenciesKind kind) const;
234+
235+
public:
236+
ModuleDependenciesCache() { }
237+
238+
ModuleDependenciesCache(const ModuleDependenciesCache &) = delete;
239+
ModuleDependenciesCache &operator=(const ModuleDependenciesCache &) = delete;
240+
241+
~ModuleDependenciesCache() {
242+
destroyClangImpl();
243+
}
244+
245+
/// Set the Clang-specific implementation data.
246+
void setClangImpl(
247+
ClangModuleDependenciesCacheImpl *clangImpl,
248+
void (*clangImplDeleter)(ClangModuleDependenciesCacheImpl *)) {
249+
destroyClangImpl();
250+
251+
this->clangImpl = clangImpl;
252+
this->clangImplDeleter = clangImplDeleter;
253+
}
254+
255+
/// Retrieve the Clang-specific implementation data;
256+
ClangModuleDependenciesCacheImpl *getClangImpl() const {
257+
return clangImpl;
258+
}
259+
260+
/// Whether we have cached dependency information for the given module.
261+
bool hasDependencies(StringRef moduleName,
262+
Optional<ModuleDependenciesKind> kind) const;
263+
264+
/// Look for module dependencies for a module with the given name.
265+
///
266+
/// \returns the cached result, or \c None if there is no cached entry.
267+
Optional<ModuleDependencies> findDependencies(
268+
StringRef moduleName,
269+
Optional<ModuleDependenciesKind> kind) const;
270+
271+
/// Record dependencies for the given module.
272+
void recordDependencies(StringRef moduleName,
273+
ModuleDependencies dependencies,
274+
ModuleDependenciesKind kind);
275+
276+
/// Reference the list of all module dependencies.
277+
const std::vector<ModuleDependencyID> &getAllModules() const {
278+
return AllModules;
279+
}
280+
};
281+
282+
}
283+
284+
#endif /* SWIFT_AST_MODULE_DEPENDENCIES_H */

include/swift/AST/ModuleLoader.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/Basic/SourceLoc.h"
2424
#include "llvm/ADT/SetVector.h"
2525
#include "llvm/ADT/SmallSet.h"
26+
#include "llvm/ADT/StringSet.h"
2627
#include "llvm/ADT/TinyPtrVector.h"
2728

2829
namespace llvm {
@@ -41,7 +42,10 @@ class ClangImporterOptions;
4142
class ClassDecl;
4243
class FileUnit;
4344
class ModuleDecl;
45+
class ModuleDependencies;
46+
class ModuleDependenciesCache;
4447
class NominalTypeDecl;
48+
class SourceFile;
4549
class TypeDecl;
4650

4751
enum class KnownProtocolKind : uint8_t;
@@ -177,6 +181,12 @@ class ModuleLoader {
177181
/// Discover overlays declared alongside this file and add infomation about
178182
/// them to it.
179183
void findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module, FileUnit *file);
184+
185+
/// Retrieve the dependencies for the given, named module, or \c None
186+
/// if no such module exists.
187+
virtual Optional<ModuleDependencies> getModuleDependencies(
188+
StringRef moduleName,
189+
ModuleDependenciesCache &cache) = 0;
180190
};
181191

182192
} // namespace swift

include/swift/Basic/FileTypes.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ TYPE("tbd", TBD, "tbd", "")
7171
// Swift section of the internal wiki.
7272
TYPE("module-trace", ModuleTrace, "trace.json", "")
7373

74+
// Complete dependency information for the given Swift files as JSON.
75+
TYPE("json-dependencies", JSONDependencies, "dependencies.json", "")
76+
7477
TYPE("index-data", IndexData, "", "")
7578
TYPE("yaml-opt-record", YAMLOptRecord, "opt.yaml", "")
7679
TYPE("bitstream-opt-record",BitstreamOptRecord, "opt.bitstream", "")

include/swift/ClangImporter/ClangImporter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ class ClangImporter final : public ClangModuleLoader {
369369

370370
void verifyAllModules() override;
371371

372+
Optional<ModuleDependencies> getModuleDependencies(
373+
StringRef moduleName, ModuleDependenciesCache &cache) override;
374+
372375
clang::TargetInfo &getTargetInfo() const override;
373376
clang::ASTContext &getClangASTContext() const override;
374377
clang::Preprocessor &getClangPreprocessor() const override;

include/swift/Frontend/FrontendOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ class FrontendOptions {
132132

133133
EmitPCM, ///< Emit precompiled Clang module from a module map
134134
DumpPCM, ///< Dump information about a precompiled Clang module
135+
136+
ScanDependencies, ///< Scan dependencies of Swift source files
135137
};
136138

137139
/// Indicates the action the user requested that the frontend perform.

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,10 @@ def sanitize_coverage_EQ : CommaJoined<["-"], "sanitize-coverage=">,
10311031
HelpText<"Specify the type of coverage instrumentation for Sanitizers and"
10321032
" additional options separated by commas">;
10331033

1034+
def scan_dependencies : Flag<["-"], "scan-dependencies">,
1035+
HelpText<"Scan dependencies of the given Swift sources">, ModeOpt,
1036+
Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>;
1037+
10341038
def enable_astscope_lookup : Flag<["-"], "enable-astscope-lookup">,
10351039
Flags<[FrontendOption]>,
10361040
HelpText<"Enable ASTScope-based unqualified name lookup">;

0 commit comments

Comments
 (0)