Skip to content

Commit 67c7064

Browse files
[Macro] Fix bugs in macro dependency scanning
Fix few issues from previous implementation from explicit module build with macros and accurate macro dependency scanning in #73421. First, there is a crash when propagating the macro dependencies. It turns out that the current macro plugin implementation doesn't need the downstream users to know about the plugin search path from the upstream dependencies. Secondly, fix a bug that the swiftinterface that has macro usage won't build because the build command doesn't inherit the plugin search path option. Finally, add JSON output for macro dependencies so it is easier to debug the macro dependencies. rdar://131214106 (cherry picked from commit da10a02)
1 parent 1009f6a commit 67c7064

File tree

8 files changed

+252
-15
lines changed

8 files changed

+252
-15
lines changed

include/swift-c/DependencyScan/DependencyScan.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ typedef struct swiftscan_module_details_s *swiftscan_module_details_t;
4646
/// Opaque container to a dependency info of a given module.
4747
typedef struct swiftscan_dependency_info_s *swiftscan_dependency_info_t;
4848

49+
50+
/// Opaque container to a macro dependency.
51+
typedef struct swiftscan_macro_dependency_s *swiftscan_macro_dependency_t;
52+
4953
/// Opaque container to an overall result of a dependency scan.
5054
typedef struct swiftscan_dependency_graph_s *swiftscan_dependency_graph_t;
5155

@@ -64,6 +68,12 @@ typedef struct {
6468
size_t count;
6569
} swiftscan_dependency_set_t;
6670

71+
/// Set of macro dependency
72+
typedef struct {
73+
swiftscan_macro_dependency_t *macro_dependencies;
74+
size_t count;
75+
} swiftscan_macro_dependency_set_t;
76+
6777
typedef enum {
6878
SWIFTSCAN_DIAGNOSTIC_SEVERITY_ERROR = 0,
6979
SWIFTSCAN_DIAGNOSTIC_SEVERITY_WARNING = 1,

include/swift/AST/ModuleDependencies.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ struct CommonSwiftTextualModuleDependencyDetails {
247247
std::vector<std::string> bridgingModuleDependencies;
248248

249249
/// The macro dependencies.
250-
llvm::StringMap<MacroPluginDependency> macroDependencies;
250+
std::map<std::string, MacroPluginDependency> macroDependencies;
251251

252252
/// The Swift frontend invocation arguments to build the Swift module from the
253253
/// interface.
@@ -315,7 +315,7 @@ class SwiftInterfaceModuleDependenciesStorage :
315315
void addMacroDependency(StringRef macroModuleName, StringRef libraryPath,
316316
StringRef executablePath) {
317317
textualModuleDetails.macroDependencies.insert(
318-
{macroModuleName, {libraryPath.str(), executablePath.str()}});
318+
{macroModuleName.str(), {libraryPath.str(), executablePath.str()}});
319319
}
320320
};
321321

@@ -372,7 +372,7 @@ class SwiftSourceModuleDependenciesStorage :
372372
void addMacroDependency(StringRef macroModuleName, StringRef libraryPath,
373373
StringRef executablePath) {
374374
textualModuleDetails.macroDependencies.insert(
375-
{macroModuleName, {libraryPath.str(), executablePath.str()}});
375+
{macroModuleName.str(), {libraryPath.str(), executablePath.str()}});
376376
}
377377
};
378378

@@ -779,7 +779,7 @@ class ModuleDependencyInfo {
779779
/// For a Source dependency, register a `Testable` import
780780
void addTestableImport(ImportPath::Module module);
781781

782-
/// For a Source dependency, register a macro dependency.
782+
/// For a Source/Textual dependency, register a macro dependency.
783783
void addMacroDependency(StringRef macroModuleName, StringRef libraryPath,
784784
StringRef executablePath);
785785

include/swift/DependencyScan/DependencyScanImpl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ struct swiftscan_dependency_info_s {
6464
swiftscan_module_details_t details;
6565
};
6666

67+
struct swiftscan_macro_dependency_s {
68+
swiftscan_string_ref_t moduleName;
69+
swiftscan_string_ref_t libraryPath;
70+
swiftscan_string_ref_t executablePath;
71+
};
72+
6773
/// Swift modules to be built from a module interface, may have a bridging
6874
/// header.
6975
typedef struct {
@@ -111,6 +117,9 @@ typedef struct {
111117

112118
/// ModuleCacheKey
113119
swiftscan_string_ref_t module_cache_key;
120+
121+
/// Macro dependecies.
122+
swiftscan_macro_dependency_set_t *macro_dependencies;
114123
} swiftscan_swift_textual_details_t;
115124

116125
/// Swift modules with only a binary module file.

lib/DependencyScan/DependencyScanJSON.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,49 @@ static void writeDependencies(llvm::raw_ostream &out,
202202
out << "\n";
203203
}
204204

205+
static void
206+
writeMacroDependencies(llvm::raw_ostream &out,
207+
const swiftscan_macro_dependency_set_t *macro_deps,
208+
unsigned indentLevel, bool trailingComma) {
209+
if (macro_deps->count == 0)
210+
return;
211+
212+
out.indent(indentLevel * 2);
213+
out << "\"macroDependencies\": ";
214+
out << "[\n";
215+
for (size_t i = 0; i < macro_deps->count; ++i) {
216+
const auto &macroInfo = *macro_deps->macro_dependencies[i];
217+
out.indent((indentLevel + 1) * 2);
218+
out << "{\n";
219+
auto entryIndentLevel = ((indentLevel + 2) * 2);
220+
out.indent(entryIndentLevel);
221+
out << "\"moduleName\": ";
222+
writeJSONValue(out, macroInfo.moduleName, indentLevel);
223+
out << ",\n";
224+
out.indent(entryIndentLevel);
225+
out << "\"libraryPath\": ";
226+
writeJSONValue(out, macroInfo.libraryPath, entryIndentLevel);
227+
out << ",\n";
228+
out.indent(entryIndentLevel);
229+
out << "\"executablePath\": ";
230+
writeJSONValue(out, macroInfo.executablePath, entryIndentLevel);
231+
out << "\n";
232+
out.indent((indentLevel + 1) * 2);
233+
out << "}";
234+
if (i != macro_deps->count - 1) {
235+
out << ",";
236+
}
237+
out << "\n";
238+
}
239+
240+
out.indent(indentLevel * 2);
241+
out << "]";
242+
243+
if (trailingComma)
244+
out << ",";
245+
out << "\n";
246+
}
247+
205248
static const swiftscan_swift_textual_details_t *
206249
getAsTextualDependencyModule(swiftscan_module_details_t details) {
207250
if (details->kind == SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL)
@@ -369,6 +412,8 @@ void writeJSON(llvm::raw_ostream &out,
369412
swiftTextualDeps->module_cache_key, 5,
370413
/*trailingComma=*/true);
371414
}
415+
writeMacroDependencies(out, swiftTextualDeps->macro_dependencies, 5,
416+
/*trailingComma=*/true);
372417
writeJSONSingleField(out, "isFramework", swiftTextualDeps->is_framework,
373418
5, commaAfterFramework);
374419
if (swiftTextualDeps->extra_pcm_args->count != 0) {

lib/DependencyScan/ScanDependencies.cpp

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "swift-c/DependencyScan/DependencyScan.h"
1314
#include "swift/Basic/PrettyStackTrace.h"
1415

1516
#include "swift/AST/ASTContext.h"
@@ -255,14 +256,6 @@ static llvm::Error resolveExplicitModuleInputs(
255256
: interfaceDepDetails->moduleCacheKey;
256257
commandLine.push_back("-swift-module-file=" + depModuleID.ModuleName + "=" +
257258
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-
});
266259
} break;
267260
case swift::ModuleDependencyKind::SwiftBinary: {
268261
auto binaryDepDetails = depInfo.getAsSwiftBinaryModule();
@@ -281,7 +274,7 @@ static llvm::Error resolveExplicitModuleInputs(
281274
{bridgingHeaderDepName, ModuleDependencyKind::Clang});
282275
const auto bridgingHeaderDepModuleDetails =
283276
optionalBridgingHeaderDepModuleInfo.getAsClangModule();
284-
commandLine.push_back("-Xcc");
277+
commandLine.push_back("-Xcc");
285278
commandLine.push_back(
286279
"-fmodule-map-file=" +
287280
remapPath(bridgingHeaderDepModuleDetails->moduleMapFile));
@@ -371,6 +364,10 @@ static llvm::Error resolveExplicitModuleInputs(
371364
llvm::for_each(
372365
textualDep->auxiliaryFiles,
373366
[&tracker](const std::string &file) { tracker->trackFile(file); });
367+
llvm::for_each(textualDep->textualModuleDetails.macroDependencies,
368+
[&tracker](const auto &entry) {
369+
tracker->trackFile(entry.second.LibraryPath);
370+
});
374371
auto root = tracker->createTreeFromDependencies();
375372
if (!root)
376373
return root.takeError();
@@ -641,6 +638,28 @@ static swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput(
641638
return diagnosticOutput;
642639
}
643640

641+
static swiftscan_macro_dependency_set_t *createMacroDependencySet(
642+
const std::map<std::string, MacroPluginDependency> &macroDeps) {
643+
swiftscan_macro_dependency_set_t *set = new swiftscan_macro_dependency_set_t;
644+
if (macroDeps.empty()) {
645+
set->count = 0;
646+
set->macro_dependencies = nullptr;
647+
return set;
648+
}
649+
set->count = macroDeps.size();
650+
set->macro_dependencies = new swiftscan_macro_dependency_t[set->count];
651+
unsigned SI = 0;
652+
for (auto &entry : macroDeps) {
653+
set->macro_dependencies[SI] = new swiftscan_macro_dependency_s;
654+
set->macro_dependencies[SI]->moduleName = create_clone(entry.first.c_str());
655+
set->macro_dependencies[SI]->libraryPath =
656+
create_clone(entry.second.LibraryPath.c_str());
657+
set->macro_dependencies[SI]->executablePath =
658+
create_clone(entry.second.ExecutablePath.c_str());
659+
}
660+
return set;
661+
}
662+
644663
static swiftscan_dependency_graph_t
645664
generateFullDependencyGraph(const CompilerInstance &instance,
646665
const DependencyScanDiagnosticCollector *diagnosticCollector,
@@ -730,7 +749,9 @@ generateFullDependencyGraph(const CompilerInstance &instance,
730749
.CASFileSystemRootID.c_str()),
731750
create_clone(swiftTextualDeps->textualModuleDetails
732751
.CASBridgingHeaderIncludeTreeRootID.c_str()),
733-
create_clone(swiftTextualDeps->moduleCacheKey.c_str())};
752+
create_clone(swiftTextualDeps->moduleCacheKey.c_str()),
753+
createMacroDependencySet(
754+
swiftTextualDeps->textualModuleDetails.macroDependencies)};
734755
} else if (swiftSourceDeps) {
735756
swiftscan_string_ref_t moduleInterfacePath = create_null();
736757
swiftscan_string_ref_t bridgingHeaderPath =
@@ -764,7 +785,9 @@ generateFullDependencyGraph(const CompilerInstance &instance,
764785
/*IncludeTree*/
765786
create_clone(swiftSourceDeps->textualModuleDetails
766787
.CASBridgingHeaderIncludeTreeRootID.c_str()),
767-
/*CacheKey*/ create_clone("")};
788+
/*CacheKey*/ create_clone(""),
789+
createMacroDependencySet(
790+
swiftSourceDeps->textualModuleDetails.macroDependencies)};
768791
} else if (swiftPlaceholderDeps) {
769792
details->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER;
770793
details->swift_placeholder_details = {

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/DiagnosticsSema.h"
2020
#include "swift/AST/FileSystem.h"
2121
#include "swift/AST/Module.h"
22+
#include "swift/AST/SearchPathOptions.h"
2223
#include "swift/Basic/Platform.h"
2324
#include "swift/Basic/StringExtras.h"
2425
#include "swift/Frontend/CachingUtils.h"
@@ -1696,6 +1697,39 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface(
16961697
genericSubInvocation.setPlatformAvailabilityInheritanceMapPath(*SearchPathOpts.PlatformAvailabilityInheritanceMapPath);
16971698
}
16981699

1700+
for (auto &entry : SearchPathOpts.PluginSearchOpts) {
1701+
switch (entry.getKind()) {
1702+
case PluginSearchOption::Kind::LoadPluginLibrary: {
1703+
auto &val = entry.get<PluginSearchOption::LoadPluginLibrary>();
1704+
GenericArgs.push_back("-load-plugin-library");
1705+
GenericArgs.push_back(ArgSaver.save(val.LibraryPath));
1706+
break;
1707+
}
1708+
case PluginSearchOption::Kind::LoadPluginExecutable: {
1709+
auto &val = entry.get<PluginSearchOption::LoadPluginExecutable>();
1710+
for (auto &moduleName : val.ModuleNames) {
1711+
GenericArgs.push_back("-load-plugin-executable");
1712+
GenericArgs.push_back(
1713+
ArgSaver.save(val.ExecutablePath + "#" + moduleName));
1714+
}
1715+
break;
1716+
}
1717+
case PluginSearchOption::Kind::PluginPath: {
1718+
auto &val = entry.get<PluginSearchOption::PluginPath>();
1719+
GenericArgs.push_back("-plugin-path");
1720+
GenericArgs.push_back(ArgSaver.save(val.SearchPath));
1721+
break;
1722+
}
1723+
case PluginSearchOption::Kind::ExternalPluginPath: {
1724+
auto &val = entry.get<PluginSearchOption::ExternalPluginPath>();
1725+
GenericArgs.push_back("-external-plugin-path");
1726+
GenericArgs.push_back(
1727+
ArgSaver.save(val.SearchPath + "#" + val.ServerPath));
1728+
break;
1729+
}
1730+
}
1731+
}
1732+
16991733
genericSubInvocation.getFrontendOptions().InputMode
17001734
= FrontendOptions::ParseInputMode::SwiftModuleInterface;
17011735
if (!SearchPathOpts.RuntimeResourcePath.empty()) {

test/CAS/macro_deps.swift

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// REQUIRES: swift_swift_parser
2+
3+
/// Test loading dependencies that has macros.
4+
// RUN: %empty-directory(%t)
5+
// RUN: split-file %s %t
6+
7+
/// Build macros.
8+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroOne) -module-name=MacroOne %t/macro-1.swift
9+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroTwo) -module-name=MacroTwo %t/macro-2.swift
10+
11+
/// Build binary module that depends on textual module that uses macro.
12+
// RUN: %target-swift-frontend -emit-module -module-cache-path %t/clang-module-cache %t/test.swift -module-name Test -o %t/include/Test.swiftmodule -I %t/include \
13+
// RUN: -O -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -swift-version 5 -external-plugin-path %t#%swift-plugin-server
14+
15+
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name MyApp -module-cache-path %t/clang-module-cache -O \
16+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -I %t/include \
17+
// RUN: %t/main.swift -o %t/deps.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t#%swift-plugin-server
18+
19+
// RUN: %{python} %S/Inputs/SwiftDepsExtractor.py %t/deps.json Foo macroDependencies | %FileCheck %s --check-prefix=FOO-DEPS
20+
// FOO-DEPS: MacroOne
21+
// FOO-DEPS-NOT: MacroTwo
22+
23+
// RUN: %{python} %S/Inputs/SwiftDepsExtractor.py %t/deps.json MyApp macroDependencies | %FileCheck %s --check-prefix=APP-DEPS
24+
// APP-DEPS: MacroTwo
25+
// APP-DEPS-NOT: MacroOne
26+
27+
/// Build all dependencies.
28+
// RUN: %S/Inputs/BuildCommandExtractor.py %t/deps.json clang:SwiftShims > %t/SwiftShims.cmd
29+
// RUN: %swift_frontend_plain @%t/SwiftShims.cmd
30+
// RUN: %S/Inputs/BuildCommandExtractor.py %t/deps.json Foo > %t/Foo.cmd
31+
// RUN: %swift_frontend_plain @%t/Foo.cmd
32+
33+
// RUN: %S/Inputs/BuildCommandExtractor.py %t/deps.json MyApp > %t/MyApp.cmd
34+
// RUN: %{python} %S/Inputs/GenerateExplicitModuleMap.py %t/deps.json > %t/map.json
35+
// RUN: llvm-cas --cas %t/cas --make-blob --data %t/map.json > %t/map.casid
36+
37+
// RUN: %target-swift-frontend -diagnostic-style=swift \
38+
// RUN: -emit-module -o %t/Test.swiftmodule -cache-compile-job -cas-path %t/cas \
39+
// RUN: -swift-version 5 -disable-implicit-swift-modules \
40+
// RUN: -external-plugin-path %t#%swift-plugin-server \
41+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
42+
// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map.casid -O \
43+
// RUN: %t/main.swift @%t/MyApp.cmd
44+
45+
//--- macro-1.swift
46+
import SwiftSyntax
47+
@_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros
48+
49+
public struct AssertMacro: ExpressionMacro {
50+
public static func expansion(
51+
of macro: some FreestandingMacroExpansionSyntax,
52+
in context: some MacroExpansionContext
53+
) -> ExprSyntax {
54+
guard let argument = macro.arguments.first?.expression else {
55+
fatalError("boom")
56+
}
57+
58+
return "assert(\(argument))"
59+
}
60+
}
61+
62+
//--- macro-2.swift
63+
import SwiftSyntax
64+
@_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros
65+
66+
public struct StringifyMacro: ExpressionMacro {
67+
public static func expansion(
68+
of macro: some FreestandingMacroExpansionSyntax,
69+
in context: some MacroExpansionContext
70+
) -> ExprSyntax {
71+
guard let argument = macro.arguments.first?.expression else {
72+
fatalError("boom")
73+
}
74+
75+
return "(\(argument), \(StringLiteralExprSyntax(content: argument.description)))"
76+
}
77+
}
78+
79+
//--- include/Foo.swiftinterface
80+
// swift-interface-format-version: 1.0
81+
// swift-module-flags: -enable-library-evolution -swift-version 5 -O -module-name Foo -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import
82+
import Swift
83+
public func foo()
84+
@freestanding(expression) public macro assert(_: Bool) = #externalMacro(module: "MacroOne", type: "AssertMacro")
85+
@inlinable
86+
public func assertFalse() {
87+
#assert(false)
88+
}
89+
90+
//--- test.swift
91+
import Foo
92+
@inlinable
93+
public func test() {
94+
#assert(true)
95+
}
96+
97+
//--- main.swift
98+
import Test
99+
@freestanding(expression) macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "MacroTwo", type: "StringifyMacro")
100+
101+
func appTest() {
102+
let str = #stringify("test")
103+
test()
104+
}

0 commit comments

Comments
 (0)