Skip to content

Commit 5a1f38b

Browse files
authored
Merge pull request #33569 from nkcsgexi/batch-scanning-protocol
DependenciesScanner: implement protocol for batch module dependencies scan
2 parents 3d61c21 + 6a6b1f8 commit 5a1f38b

File tree

10 files changed

+225
-13
lines changed

10 files changed

+225
-13
lines changed

include/swift/AST/ASTContext.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,12 @@ class ASTContext final {
739739
ModuleDependenciesCache &cache,
740740
InterfaceSubContextDelegate &delegate);
741741

742+
/// Retrieve the module dependencies for the Swift module with the given name.
743+
Optional<ModuleDependencies> getSwiftModuleDependencies(
744+
StringRef moduleName,
745+
ModuleDependenciesCache &cache,
746+
InterfaceSubContextDelegate &delegate);
747+
742748
/// Load extensions to the given nominal type from the external
743749
/// module loaders.
744750
///

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,14 @@ ERROR(placeholder_dependency_module_map_corrupted,none,
274274
"Swift placeholder dependency module map from %0 is malformed",
275275
(StringRef))
276276

277+
ERROR(batch_scan_input_file_missing,none,
278+
"cannot open batch dependencies scan input file from %0",
279+
(StringRef))
280+
281+
ERROR(batch_scan_input_file_corrupted,none,
282+
"batch dependencies scan input file from %0 is malformed",
283+
(StringRef))
284+
277285
REMARK(default_previous_install_name, none,
278286
"default previous install name for %0 is %1", (StringRef, StringRef))
279287

include/swift/AST/SearchPathOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ class SearchPathOptions {
9393

9494
/// A map of placeholder Swift module dependency information.
9595
std::string PlaceholderDependencyModuleMap;
96+
97+
/// A file containing modules we should perform batch scanning.
98+
std::string BatchScanInputFilePath;
9699
private:
97100
static StringRef
98101
pathStringFromFrameworkSearchPath(const FrameworkSearchPath &next) {

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ def explict_swift_module_map
228228
def placeholder_dependency_module_map
229229
: Separate<["-"], "placeholder-dependency-module-map-file">, MetaVarName<"<path>">,
230230
HelpText<"Specify a JSON file containing information of external Swift module dependencies">;
231+
232+
def batch_scan_input_file
233+
: Separate<["-"], "batch-scan-input-file">, MetaVarName<"<path>">,
234+
HelpText<"Specify a JSON file containing modules to perform batch dependencies scanning">;
231235
}
232236

233237

lib/AST/ASTContext.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,21 @@ Optional<ModuleDependencies> ASTContext::getModuleDependencies(
15161516
return None;
15171517
}
15181518

1519+
Optional<ModuleDependencies>
1520+
ASTContext::getSwiftModuleDependencies(StringRef moduleName,
1521+
ModuleDependenciesCache &cache,
1522+
InterfaceSubContextDelegate &delegate) {
1523+
for (auto &loader : getImpl().ModuleLoaders) {
1524+
if (loader.get() == getImpl().TheClangModuleLoader)
1525+
continue;
1526+
1527+
if (auto dependencies = loader->getModuleDependencies(moduleName, cache,
1528+
delegate))
1529+
return dependencies;
1530+
}
1531+
return None;
1532+
}
1533+
15191534
void ASTContext::loadExtensions(NominalTypeDecl *nominal,
15201535
unsigned previousGeneration) {
15211536
PrettyStackTraceDecl stackTrace("loading extensions for", nominal);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,8 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts,
926926
}
927927
if (const Arg *A = Args.getLastArg(OPT_placeholder_dependency_module_map))
928928
Opts.PlaceholderDependencyModuleMap = A->getValue();
929-
929+
if (const Arg *A = Args.getLastArg(OPT_batch_scan_input_file))
930+
Opts.BatchScanInputFilePath = A->getValue();
930931
// Opts.RuntimeIncludePath is set by calls to
931932
// setRuntimeIncludePath() or setMainExecutablePath().
932933
// Opts.RuntimeImportPath is set by calls to

lib/FrontendTool/FrontendTool.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,8 +1834,14 @@ static bool performCompile(CompilerInstance &Instance,
18341834
return finishPipeline(Context.hadError());
18351835
}
18361836

1837-
if (Action == FrontendOptions::ActionType::ScanDependencies)
1838-
return finishPipeline(scanDependencies(Instance));
1837+
if (Action == FrontendOptions::ActionType::ScanDependencies) {
1838+
auto batchScanInput = Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath;
1839+
if (batchScanInput.empty())
1840+
return finishPipeline(scanDependencies(Instance));
1841+
else
1842+
return finishPipeline(batchScanModuleDependencies(Instance,
1843+
batchScanInput));
1844+
}
18391845

18401846
if (Action == FrontendOptions::ActionType::ScanClangDependencies)
18411847
return finishPipeline(scanClangDependencies(Instance));

lib/FrontendTool/ScanDependencies.cpp

Lines changed: 135 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,103 @@
3232
#include "llvm/ADT/StringRef.h"
3333
#include "llvm/ADT/StringSet.h"
3434
#include "llvm/Support/FileSystem.h"
35+
#include "llvm/Support/StringSaver.h"
36+
#include "llvm/Support/YAMLTraits.h"
37+
#include "llvm/Support/YAMLParser.h"
3538
#include <set>
3639

3740
using namespace swift;
41+
using namespace llvm::yaml;
3842

3943
namespace {
44+
struct BatchScanInput {
45+
StringRef moduleName;
46+
StringRef arguments;
47+
StringRef outputPath;
48+
bool isSwift;
49+
};
50+
51+
static std::string getScalaNodeText(Node *N) {
52+
SmallString<32> Buffer;
53+
return cast<ScalarNode>(N)->getValue(Buffer).str();
54+
}
55+
56+
/// Parse an entry like this, where the "platforms" key-value pair is optional:
57+
/// {
58+
/// "swiftModuleName": "Foo",
59+
/// "arguments": "-target 10.15",
60+
/// "output": "../Foo.json"
61+
/// },
62+
static bool parseBatchInputEntries(ASTContext &Ctx, llvm::StringSaver &saver,
63+
Node *Node, std::vector<BatchScanInput> &result) {
64+
auto *SN = cast<SequenceNode>(Node);
65+
if (!SN)
66+
return true;
67+
for (auto It = SN->begin(); It != SN->end(); ++It) {
68+
auto *MN = cast<MappingNode>(&*It);
69+
BatchScanInput entry;
70+
Optional<std::set<int8_t>> Platforms;
71+
for (auto &Pair: *MN) {
72+
auto Key = getScalaNodeText(Pair.getKey());
73+
auto* Value = Pair.getValue();
74+
if (Key == "clangModuleName") {
75+
entry.moduleName = saver.save(getScalaNodeText(Value));
76+
entry.isSwift = false;
77+
} else if (Key == "swiftModuleName") {
78+
entry.moduleName = saver.save(getScalaNodeText(Value));
79+
entry.isSwift = true;
80+
} else if (Key == "arguments") {
81+
entry.arguments = saver.save(getScalaNodeText(Value));
82+
} else if (Key == "output") {
83+
entry.outputPath = saver.save(getScalaNodeText(Value));
84+
} else {
85+
// Future proof.
86+
continue;
87+
}
88+
}
89+
if (entry.moduleName.empty())
90+
return true;
91+
if (entry.outputPath.empty())
92+
return true;
93+
result.emplace_back(std::move(entry));
94+
}
95+
return false;
96+
}
4097

98+
static Optional<std::vector<BatchScanInput>>
99+
parseBatchScanInputFile(ASTContext &ctx, StringRef batchInputPath,
100+
llvm::StringSaver &saver) {
101+
assert(!batchInputPath.empty());
102+
namespace yaml = llvm::yaml;
103+
std::vector<BatchScanInput> result;
104+
105+
// Load the input file.
106+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
107+
llvm::MemoryBuffer::getFile(batchInputPath);
108+
if (!FileBufOrErr) {
109+
ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_missing,
110+
batchInputPath);
111+
return None;
112+
}
113+
StringRef Buffer = FileBufOrErr->get()->getBuffer();
114+
115+
// Use a new source manager instead of the one from ASTContext because we
116+
// don't want the Json file to be persistent.
117+
SourceManager SM;
118+
yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, batchInputPath),
119+
SM.getLLVMSourceMgr());
120+
for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) {
121+
assert(DI != Stream.end() && "Failed to read a document");
122+
yaml::Node *N = DI->getRoot();
123+
assert(N && "Failed to find a root");
124+
if (parseBatchInputEntries(ctx, saver, N, result)) {
125+
ctx.Diags.diagnose(SourceLoc(), diag::batch_scan_input_file_corrupted,
126+
batchInputPath);
127+
return None;
128+
}
129+
}
130+
return result;
131+
}
41132
}
42133

43134
/// Find all of the imported Clang modules starting with the given module name.
@@ -533,15 +624,17 @@ static bool diagnoseCycle(CompilerInstance &instance,
533624
return false;
534625
}
535626

536-
bool swift::scanClangDependencies(CompilerInstance &instance) {
627+
static bool scanModuleDependencies(CompilerInstance &instance,
628+
StringRef moduleName,
629+
StringRef arguments,
630+
bool isClang,
631+
StringRef outputPath) {
537632
ASTContext &ctx = instance.getASTContext();
538-
ModuleDecl *mainModule = instance.getMainModule();
539633
auto &FEOpts = instance.getInvocation().getFrontendOptions();
540634
ModuleInterfaceLoaderOptions LoaderOpts(FEOpts);
541635
auto ModuleCachePath = getModuleCachePathFromClang(ctx
542636
.getClangModuleLoader()->getClangInstance());
543637

544-
StringRef mainModuleName = mainModule->getNameStr();
545638
llvm::SetVector<ModuleDependencyID, std::vector<ModuleDependencyID>,
546639
std::set<ModuleDependencyID>> allModules;
547640
// Create the module dependency cache.
@@ -555,16 +648,23 @@ bool swift::scanClangDependencies(CompilerInstance &instance) {
555648
FEOpts.PrebuiltModuleCachePath,
556649
FEOpts.SerializeModuleInterfaceDependencyHashes,
557650
FEOpts.shouldTrackSystemDependencies());
558-
// Loading the clang module using Clang importer.
559-
// This action will populate the cache with the main module's dependencies.
560-
auto rootDeps = static_cast<ClangImporter*>(ctx.getClangModuleLoader())
561-
->getModuleDependencies(mainModuleName, cache, ASTDelegate);
651+
Optional<ModuleDependencies> rootDeps;
652+
if (isClang) {
653+
// Loading the clang module using Clang importer.
654+
// This action will populate the cache with the main module's dependencies.
655+
rootDeps = ctx.getModuleDependencies(moduleName, /*IsClang*/true, cache,
656+
ASTDelegate);
657+
} else {
658+
// Loading the swift module's dependencies.
659+
rootDeps = ctx.getSwiftModuleDependencies(moduleName, cache, ASTDelegate);
660+
}
562661
if (!rootDeps.hasValue()) {
563662
// We cannot find the clang module, abort.
564663
return true;
565664
}
566665
// Add the main module.
567-
allModules.insert({mainModuleName.str(), ModuleDependenciesKind::Clang});
666+
allModules.insert({moduleName.str(), isClang ? ModuleDependenciesKind::Clang:
667+
ModuleDependenciesKind::Swift});
568668

569669
// Explore the dependencies of every module.
570670
for (unsigned currentModuleIdx = 0;
@@ -576,13 +676,38 @@ bool swift::scanClangDependencies(CompilerInstance &instance) {
576676
allModules.insert(discoveredModules.begin(), discoveredModules.end());
577677
}
578678
// Write out the JSON description.
579-
std::string path = FEOpts.InputsAndOutputs.getSingleOutputFilename();
580679
std::error_code EC;
581-
llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None);
680+
llvm::raw_fd_ostream out(outputPath, EC, llvm::sys::fs::F_None);
582681
writeJSON(out, instance, cache, ASTDelegate, allModules.getArrayRef());
583682
return false;
584683
}
585684

685+
bool swift::scanClangDependencies(CompilerInstance &instance) {
686+
return scanModuleDependencies(instance,
687+
instance.getMainModule()->getNameStr(),
688+
StringRef(),
689+
/*isClang*/true,
690+
instance.getInvocation().getFrontendOptions()
691+
.InputsAndOutputs.getSingleOutputFilename());
692+
}
693+
694+
bool swift::batchScanModuleDependencies(CompilerInstance &instance,
695+
llvm::StringRef batchInputFile) {
696+
(void)instance.getMainModule();
697+
llvm::BumpPtrAllocator alloc;
698+
llvm::StringSaver saver(alloc);
699+
auto results = parseBatchScanInputFile(instance.getASTContext(),
700+
batchInputFile, saver);
701+
if (!results.hasValue())
702+
return true;
703+
for (auto &entry: *results) {
704+
if (scanModuleDependencies(instance, entry.moduleName, entry.arguments,
705+
!entry.isSwift, entry.outputPath))
706+
return true;
707+
}
708+
return false;
709+
}
710+
586711
bool swift::scanDependencies(CompilerInstance &instance) {
587712
ASTContext &Context = instance.getASTContext();
588713
ModuleDecl *mainModule = instance.getMainModule();

lib/FrontendTool/ScanDependencies.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@
1313
#ifndef SWIFT_FRONTENDTOOL_SCANDEPENDENCIES_H
1414
#define SWIFT_FRONTENDTOOL_SCANDEPENDENCIES_H
1515

16+
#include "llvm/ADT/StringRef.h"
17+
1618
namespace swift {
1719

1820
class CompilerInstance;
1921

22+
/// Batch scan the dependencies for modules specified in \c batchInputFile.
23+
bool batchScanModuleDependencies(CompilerInstance &instance, llvm::StringRef batchInputFile);
24+
2025
/// Scans the dependencies of the main module of \c instance.
2126
bool scanDependencies(CompilerInstance &instance);
2227

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/inputs)
3+
// RUN: %empty-directory(%t/outputs)
4+
// RUN: mkdir -p %t/clang-module-cache
5+
6+
// RUN: echo "[{" > %/t/inputs/input.json
7+
// RUN: echo "\"swiftModuleName\": \"F\"," >> %/t/inputs/input.json
8+
// RUN: echo "\"arguments\": \"-target x86_64-apple-macosx10.9\"," >> %/t/inputs/input.json
9+
// RUN: echo "\"output\": \"%/t/outputs/F.swiftmodule.json\"" >> %/t/inputs/input.json
10+
// RUN: echo "}," >> %/t/inputs/input.json
11+
// RUN: echo "{" >> %/t/inputs/input.json
12+
// RUN: echo "\"clangModuleName\": \"F\"," >> %/t/inputs/input.json
13+
// RUN: echo "\"arguments\": \"-target x86_64-apple-macosx10.9\"," >> %/t/inputs/input.json
14+
// RUN: echo "\"output\": \"%/t/outputs/F.pcm.json\"" >> %/t/inputs/input.json
15+
// RUN: echo "}]" >> %/t/inputs/input.json
16+
17+
// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -batch-scan-input-file %/t/inputs/input.json
18+
19+
// Check the contents of the JSON output
20+
// RUN: %FileCheck %s -check-prefix=CHECK-PCM < %t/outputs/F.pcm.json
21+
// RUN: %FileCheck %s -check-prefix=CHECK-SWIFT < %t/outputs/F.swiftmodule.json
22+
23+
// CHECK-PCM: {
24+
// CHECK-PCM-NEXT: "mainModuleName": "F",
25+
// CHECK-PCM-NEXT: "modules": [
26+
// CHECK-PCM-NEXT: {
27+
// CHECK-PCM-NEXT: "clang": "F"
28+
// CHECK-PCM-NEXT: },
29+
// CHECK-PCM-NEXT: {
30+
// CHECK-PCM-NEXT: "modulePath": "F.pcm",
31+
32+
// CHECK-SWIFT: {
33+
// CHECK-SWIFT-NEXT: "mainModuleName": "F",
34+
// CHECK-SWIFT-NEXT: "modules": [
35+
// CHECK-SWIFT-NEXT: {
36+
// CHECK-SWIFT-NEXT: "swift": "F"
37+
// CHECK-SWIFT-NEXT: },
38+
// CHECK-SWIFT-NEXT: {
39+
// CHECK-SWIFT-NEXT: "modulePath": "F.swiftmodule",

0 commit comments

Comments
 (0)