Skip to content

Commit 1009f6a

Browse files
[ScanDependencies] Get accurate macro dependency
Build an accurate macro dependency for swift caching. Specifically, do not include not used macro plugins into the dependency, which might cause false negatives for cache hits. This also builds the foundation for future improvement when dependency scanning will determine the macro plugin to load and swift-frontend do not need to redo the work. rdar://127116512 (cherry picked from commit 4125e8c)
1 parent 6741653 commit 1009f6a

File tree

4 files changed

+107
-82
lines changed

4 files changed

+107
-82
lines changed

include/swift/AST/ModuleDependencies.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ enum class ModuleDependencyKind : int8_t {
8888
LastKind = SwiftPlaceholder + 1
8989
};
9090

91+
/// This is used to idenfity a specific macro plugin dependency.
92+
struct MacroPluginDependency {
93+
std::string LibraryPath;
94+
std::string ExecutablePath;
95+
};
96+
9197
/// This is used to identify a specific module.
9298
struct ModuleDependencyID {
9399
std::string ModuleName;
@@ -240,6 +246,9 @@ struct CommonSwiftTextualModuleDependencyDetails {
240246
/// (Clang) modules on which the bridging header depends.
241247
std::vector<std::string> bridgingModuleDependencies;
242248

249+
/// The macro dependencies.
250+
llvm::StringMap<MacroPluginDependency> macroDependencies;
251+
243252
/// The Swift frontend invocation arguments to build the Swift module from the
244253
/// interface.
245254
std::vector<std::string> buildCommandLine;
@@ -302,6 +311,12 @@ class SwiftInterfaceModuleDependenciesStorage :
302311
void updateCommandLine(const std::vector<std::string> &newCommandLine) {
303312
textualModuleDetails.buildCommandLine = newCommandLine;
304313
}
314+
315+
void addMacroDependency(StringRef macroModuleName, StringRef libraryPath,
316+
StringRef executablePath) {
317+
textualModuleDetails.macroDependencies.insert(
318+
{macroModuleName, {libraryPath.str(), executablePath.str()}});
319+
}
305320
};
306321

307322
/// Describes the dependencies of a Swift module
@@ -353,6 +368,12 @@ class SwiftSourceModuleDependenciesStorage :
353368
void addTestableImport(ImportPath::Module module) {
354369
testableImports.insert(module.front().Item.str());
355370
}
371+
372+
void addMacroDependency(StringRef macroModuleName, StringRef libraryPath,
373+
StringRef executablePath) {
374+
textualModuleDetails.macroDependencies.insert(
375+
{macroModuleName, {libraryPath.str(), executablePath.str()}});
376+
}
356377
};
357378

358379
/// Describes the dependencies of a pre-built Swift module (with no
@@ -758,6 +779,10 @@ class ModuleDependencyInfo {
758779
/// For a Source dependency, register a `Testable` import
759780
void addTestableImport(ImportPath::Module module);
760781

782+
/// For a Source dependency, register a macro dependency.
783+
void addMacroDependency(StringRef macroModuleName, StringRef libraryPath,
784+
StringRef executablePath);
785+
761786
/// Whether or not a queried module name is a `@Testable` import dependency
762787
/// of this module. Can only return `true` for Swift source modules.
763788
bool isTestableImport(StringRef moduleName) const;

lib/AST/ModuleDependencies.cpp

Lines changed: 54 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "swift/AST/Decl.h"
1818
#include "swift/AST/DiagnosticsFrontend.h"
1919
#include "swift/AST/DiagnosticsSema.h"
20+
#include "swift/AST/MacroDefinition.h"
21+
#include "swift/AST/PluginLoader.h"
2022
#include "swift/AST/SourceFile.h"
2123
#include "swift/Frontend/Frontend.h"
2224
#include "llvm/CAS/CASProvidingFileSystem.h"
@@ -103,6 +105,21 @@ void ModuleDependencyInfo::addTestableImport(ImportPath::Module module) {
103105
dyn_cast<SwiftSourceModuleDependenciesStorage>(storage.get())->addTestableImport(module);
104106
}
105107

108+
void ModuleDependencyInfo::addMacroDependency(StringRef macroModuleName,
109+
StringRef libraryPath,
110+
StringRef executablePath) {
111+
if (auto swiftSourceStorage =
112+
dyn_cast<SwiftSourceModuleDependenciesStorage>(storage.get()))
113+
swiftSourceStorage->addMacroDependency(macroModuleName, libraryPath,
114+
executablePath);
115+
else if (auto swiftInterfaceStorage =
116+
dyn_cast<SwiftInterfaceModuleDependenciesStorage>(storage.get()))
117+
swiftInterfaceStorage->addMacroDependency(macroModuleName, libraryPath,
118+
executablePath);
119+
else
120+
llvm_unreachable("Unexpected dependency kind");
121+
}
122+
106123
bool ModuleDependencyInfo::isTestableImport(StringRef moduleName) const {
107124
if (auto swiftSourceDepStorage = getAsSwiftSourceModule())
108125
return swiftSourceDepStorage->testableImports.contains(moduleName);
@@ -183,35 +200,45 @@ void ModuleDependencyInfo::addModuleImports(
183200
SmallVector<Decl *, 32> decls;
184201
sourceFile.getTopLevelDecls(decls);
185202
for (auto decl : decls) {
186-
auto importDecl = dyn_cast<ImportDecl>(decl);
187-
if (!importDecl)
188-
continue;
189-
190-
ImportPath::Builder scratch;
191-
auto realPath = importDecl->getRealModulePath(scratch);
192-
193-
// Explicit 'Builtin' import is not a part of the module's
194-
// dependency set, does not exist on the filesystem,
195-
// and is resolved within the compiler during compilation.
196-
SmallString<64> importedModuleName;
197-
realPath.getString(importedModuleName);
198-
if (importedModuleName == BUILTIN_NAME)
199-
continue;
200-
201-
// Ignore/diagnose tautological imports akin to import resolution
202-
if (!swift::dependencies::checkImportNotTautological(
203-
realPath, importDecl->getLoc(), sourceFile,
204-
importDecl->isExported()))
205-
continue;
203+
if (auto importDecl = dyn_cast<ImportDecl>(decl)) {
204+
ImportPath::Builder scratch;
205+
auto realPath = importDecl->getRealModulePath(scratch);
206+
207+
// Explicit 'Builtin' import is not a part of the module's
208+
// dependency set, does not exist on the filesystem,
209+
// and is resolved within the compiler during compilation.
210+
SmallString<64> importedModuleName;
211+
realPath.getString(importedModuleName);
212+
if (importedModuleName == BUILTIN_NAME)
213+
continue;
206214

207-
addModuleImport(realPath, &alreadyAddedModules,
208-
sourceManager, importDecl->getLoc());
215+
// Ignore/diagnose tautological imports akin to import resolution
216+
if (!swift::dependencies::checkImportNotTautological(
217+
realPath, importDecl->getLoc(), sourceFile,
218+
importDecl->isExported()))
219+
continue;
209220

210-
// Additionally, keep track of which dependencies of a Source
211-
// module are `@Testable`.
212-
if (getKind() == swift::ModuleDependencyKind::SwiftSource &&
213-
importDecl->isTestable())
214-
addTestableImport(realPath);
221+
addModuleImport(realPath, &alreadyAddedModules, sourceManager,
222+
importDecl->getLoc());
223+
224+
// Additionally, keep track of which dependencies of a Source
225+
// module are `@Testable`.
226+
if (getKind() == swift::ModuleDependencyKind::SwiftSource &&
227+
importDecl->isTestable())
228+
addTestableImport(realPath);
229+
} else if (auto macroDecl = dyn_cast<MacroDecl>(decl)) {
230+
auto macroDef = macroDecl->getDefinition();
231+
auto &ctx = macroDecl->getASTContext();
232+
if (macroDef.kind != MacroDefinition::Kind::External)
233+
continue;
234+
auto external = macroDef.getExternalMacro();
235+
PluginLoader &loader = ctx.getPluginLoader();
236+
auto &entry = loader.lookupPluginByModuleName(external.moduleName);
237+
if (entry.libraryPath.empty() && entry.executablePath.empty())
238+
continue;
239+
addMacroDependency(external.moduleName.str(), entry.libraryPath,
240+
entry.executablePath);
241+
}
215242
}
216243

217244
auto fileName = sourceFile.getFilename();
@@ -535,58 +562,6 @@ void SwiftDependencyTracker::addCommonSearchPathDeps(
535562
// Add VFSOverlay file.
536563
for (auto &Overlay: Opts.VFSOverlayFiles)
537564
FS->status(Overlay);
538-
539-
// Add plugin dylibs from the toolchain only by look through the plugin search
540-
// directory.
541-
auto recordFiles = [&](StringRef Path) {
542-
std::error_code EC;
543-
for (auto I = FS->dir_begin(Path, EC);
544-
!EC && I != llvm::vfs::directory_iterator(); I = I.increment(EC)) {
545-
if (I->type() != llvm::sys::fs::file_type::regular_file)
546-
continue;
547-
#if defined(_WIN32)
548-
constexpr StringRef libPrefix{};
549-
constexpr StringRef libSuffix = ".dll";
550-
#else
551-
constexpr StringRef libPrefix = "lib";
552-
constexpr StringRef libSuffix = LTDL_SHLIB_EXT;
553-
#endif
554-
StringRef filename = llvm::sys::path::filename(I->path());
555-
if (filename.starts_with(libPrefix) && filename.ends_with(libSuffix))
556-
FS->status(I->path());
557-
}
558-
};
559-
for (auto &entry : Opts.PluginSearchOpts) {
560-
switch (entry.getKind()) {
561-
562-
// '-load-plugin-library <library path>'.
563-
case PluginSearchOption::Kind::LoadPluginLibrary: {
564-
auto &val = entry.get<PluginSearchOption::LoadPluginLibrary>();
565-
FS->status(val.LibraryPath);
566-
break;
567-
}
568-
569-
// '-load-plugin-executable <executable path>#<module name>, ...'.
570-
case PluginSearchOption::Kind::LoadPluginExecutable: {
571-
// We don't have executable plugin in toolchain.
572-
break;
573-
}
574-
575-
// '-plugin-path <library search path>'.
576-
case PluginSearchOption::Kind::PluginPath: {
577-
auto &val = entry.get<PluginSearchOption::PluginPath>();
578-
recordFiles(val.SearchPath);
579-
break;
580-
}
581-
582-
// '-external-plugin-path <library search path>#<server path>'.
583-
case PluginSearchOption::Kind::ExternalPluginPath: {
584-
auto &val = entry.get<PluginSearchOption::ExternalPluginPath>();
585-
recordFiles(val.SearchPath);
586-
break;
587-
}
588-
}
589-
}
590565
}
591566

592567
void SwiftDependencyTracker::startTracking() {

lib/DependencyScan/ScanDependencies.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ static llvm::Error resolveExplicitModuleInputs(
243243
return E;
244244

245245
std::vector<std::string> commandLine = resolvingDepInfo.getCommandline();
246+
auto dependencyInfoCopy = resolvingDepInfo;
246247
for (const auto &depModuleID : dependencies) {
247248
const auto &depInfo = cache.findKnownDependency(depModuleID);
248249
switch (depModuleID.Kind) {
@@ -254,6 +255,14 @@ static llvm::Error resolveExplicitModuleInputs(
254255
: interfaceDepDetails->moduleCacheKey;
255256
commandLine.push_back("-swift-module-file=" + depModuleID.ModuleName + "=" +
256257
path);
258+
// Add the exported macro from interface into current module.
259+
llvm::for_each(
260+
interfaceDepDetails->textualModuleDetails.macroDependencies,
261+
[&](const auto &entry) {
262+
dependencyInfoCopy.addMacroDependency(entry.first(),
263+
entry.second.LibraryPath,
264+
entry.second.ExecutablePath);
265+
});
257266
} break;
258267
case swift::ModuleDependencyKind::SwiftBinary: {
259268
auto binaryDepDetails = depInfo.getAsSwiftBinaryModule();
@@ -318,7 +327,6 @@ static llvm::Error resolveExplicitModuleInputs(
318327
}
319328

320329
// Update the dependency in the cache with the modified command-line.
321-
auto dependencyInfoCopy = resolvingDepInfo;
322330
if (resolvingDepInfo.isSwiftInterfaceModule() ||
323331
resolvingDepInfo.isClangModule()) {
324332
if (service.hasPathMapping())
@@ -344,6 +352,10 @@ static llvm::Error resolveExplicitModuleInputs(
344352
llvm::for_each(
345353
sourceDep->auxiliaryFiles,
346354
[&tracker](const std::string &file) { tracker->trackFile(file); });
355+
llvm::for_each(sourceDep->textualModuleDetails.macroDependencies,
356+
[&tracker](const auto &entry) {
357+
tracker->trackFile(entry.second.LibraryPath);
358+
});
347359
auto root = tracker->createTreeFromDependencies();
348360
if (!root)
349361
return root.takeError();

test/CAS/macro_plugin_external.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
// RUN: %empty-directory(%t)
77
// RUN: %empty-directory(%t/plugins)
8+
// RUN: split-file %s %t
89
//
910
//== Build the plugin library
1011
// RUN: %host-build-swift \
@@ -15,9 +16,17 @@
1516
// RUN: %S/../Macros/Inputs/syntax_macro_definitions.swift \
1617
// RUN: -g -no-toolchain-stdlib-rpath
1718

19+
/// No macro plugin when macro not used.
1820
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name MyApp -module-cache-path %t/clang-module-cache -O \
1921
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
20-
// RUN: %s -o %t/deps.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t/plugins#%swift-plugin-server
22+
// RUN: %t/nomacro.swift -o %t/deps1.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t/plugins#%swift-plugin-server
23+
// RUN: %S/Inputs/SwiftDepsExtractor.py %t/deps1.json MyApp casFSRootID > %t/no_macro_fs.casid
24+
// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/no_macro_fs.casid | %FileCheck %s --check-prefix=NO-MACRO
25+
// NO-MACRO-NOT: MacroDefinition
26+
27+
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name MyApp -module-cache-path %t/clang-module-cache -O \
28+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
29+
// RUN: %t/macro.swift -o %t/deps.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t/plugins#%swift-plugin-server
2130

2231
// RUN: %S/Inputs/SwiftDepsExtractor.py %t/deps.json MyApp casFSRootID > %t/fs.casid
2332
// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/fs.casid | %FileCheck %s --check-prefix=FS
@@ -37,8 +46,12 @@
3746
// RUN: -external-plugin-path %t/plugins/#%swift-plugin-server \
3847
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
3948
// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map.casid \
40-
// RUN: %s @%t/MyApp.cmd
49+
// RUN: %t/macro.swift @%t/MyApp.cmd
50+
51+
//--- nomacro.swift
52+
func test() {}
4153

54+
//--- macro.swift
4255
@attached(extension, conformances: P, names: named(requirement))
4356
macro DelegatedConformance() = #externalMacro(module: "MacroDefinition", type: "DelegatedConformanceViaExtensionMacro")
4457

0 commit comments

Comments
 (0)