Skip to content

Commit acd72ef

Browse files
authored
Merge pull request swiftlang#63460 from artemcm/ExplicitDependenciesOnCommandLine
[Dependency Scanner] Produce complete compile commands for building explicit Swift interface dependencies
2 parents 4ceec83 + be3812d commit acd72ef

15 files changed

+379
-78
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ ERROR(error_cannot_direct_cc1_pcm_build_in_mode,none,
110110
"'-direct-clang-cc1-module-build' only supported when building a PCM ('-emit-pcm')'", ())
111111
ERROR(error_unsupported_option_argument,none,
112112
"unsupported argument '%1' to option '%0'", (StringRef, StringRef))
113+
ERROR(error_swift_module_file_requires_delimeter,none,
114+
"-swift-module-file= argument requires format <name>=<path>, got: '%0'", (StringRef))
113115
ERROR(error_immediate_mode_missing_stdlib,none,
114116
"could not load the swift standard library", ())
115117
ERROR(error_immediate_mode_missing_library,none,

include/swift/AST/ModuleDependencies.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class SwiftInterfaceModuleDependenciesStorage :
150150

151151
/// The Swift frontend invocation arguments to build the Swift module from the
152152
/// interface.
153-
const std::vector<std::string> buildCommandLine;
153+
std::vector<std::string> buildCommandLine;
154154

155155
/// The hash value that will be used for the generated module
156156
const std::string contextHash;
@@ -186,6 +186,10 @@ class SwiftInterfaceModuleDependenciesStorage :
186186
static bool classof(const ModuleDependencyInfoStorageBase *base) {
187187
return base->dependencyKind == ModuleDependencyKind::SwiftInterface;
188188
}
189+
190+
void updateCommandLine(const std::vector<std::string> &newCommandLine) {
191+
buildCommandLine = newCommandLine;
192+
}
189193
};
190194

191195
/// Describes the dependencies of a Swift module
@@ -433,6 +437,11 @@ class ModuleDependencyInfo {
433437
storage->resolvedModuleDependencies.assign(dependencyIDs.begin(), dependencyIDs.end());
434438
}
435439

440+
void updateCommandLine(const std::vector<std::string> &newCommandLine) {
441+
assert(isSwiftInterfaceModule() && "Can only update command line on Swift interface dependency");
442+
cast<SwiftInterfaceModuleDependenciesStorage>(storage.get())->updateCommandLine(newCommandLine);
443+
}
444+
436445
bool isResolved() const {
437446
return storage->resolved;
438447
}
@@ -752,4 +761,17 @@ class ModuleDependenciesCache {
752761

753762
} // namespace swift
754763

764+
namespace std {
765+
template <>
766+
struct hash<swift::ModuleDependencyID> {
767+
using UnderlyingKindType = std::underlying_type<swift::ModuleDependencyKind>::type;
768+
std::size_t operator()(const swift::ModuleDependencyID &id) const {
769+
auto underlyingKindValue = static_cast<UnderlyingKindType>(id.second);
770+
771+
return (hash<string>()(id.first) ^
772+
(hash<UnderlyingKindType>()(underlyingKindValue)));
773+
}
774+
};
775+
} // namespace std
776+
755777
#endif /* SWIFT_AST_MODULE_DEPENDENCIES_H */

include/swift/AST/SearchPathOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ class SearchPathOptions {
355355
/// A map of explicit Swift module information.
356356
std::string ExplicitSwiftModuleMap;
357357

358+
/// Module inputs specified with -swift-module-input,
359+
/// <ModuleName, Path to .swiftmodule file>
360+
std::vector<std::pair<std::string, std::string>> ExplicitSwiftModuleInputs;
361+
358362
/// A map of placeholder Swift module dependency information.
359363
std::string PlaceholderDependencyModuleMap;
360364

include/swift/Frontend/ModuleInterfaceLoader.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ class SearchPathOptions;
129129
class CompilerInvocation;
130130

131131
/// A ModuleLoader that loads explicitly built Swift modules specified via
132-
/// -swift-module-file
132+
/// -swift-module-file or modules found in a provided
133+
/// -explicit-swift-module-map-file JSON input.
133134
class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase {
134135
explicit ExplicitSwiftModuleLoader(ASTContext &ctx, DependencyTracker *tracker,
135136
ModuleLoadingMode loadMode,
@@ -164,6 +165,7 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase {
164165
create(ASTContext &ctx,
165166
DependencyTracker *tracker, ModuleLoadingMode loadMode,
166167
StringRef ExplicitSwiftModuleMap,
168+
const std::vector<std::pair<std::string, std::string>> &ExplicitSwiftModuleInputs,
167169
bool IgnoreSwiftSourceInfoFile);
168170

169171
/// Append visible module names to \p names. Note that names are possibly

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ def disable_typo_correction : Flag<["-"], "disable-typo-correction">,
185185
def disable_implicit_swift_modules: Flag<["-"], "disable-implicit-swift-modules">,
186186
HelpText<"Disable building Swift modules implicitly by the compiler">;
187187

188+
def swift_module_file: Joined<["-"], "swift-module-file=">,
189+
MetaVarName<"<name>=<path>">,
190+
HelpText<"Specify Swift module input explicitly built from textual interface">;
191+
188192
def explicit_swift_module_map
189193
: Separate<["-"], "explicit-swift-module-map-file">, MetaVarName<"<path>">,
190194
HelpText<"Specify a JSON file containing information of explicit Swift modules">;

lib/ClangImporter/ClangModuleDependencyScanner.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,15 @@ void ClangImporter::recordModuleDependencies(
153153
// We are using Swift frontend mode.
154154
swiftArgs.push_back("-frontend");
155155

156-
// We pass the entire argument list via -Xcc, so the invocation should
157-
// use extra clang options alone.
158-
swiftArgs.push_back("-only-use-extra-clang-opts");
159-
160156
// Swift frontend action: -emit-pcm
161157
swiftArgs.push_back("-emit-pcm");
162158
swiftArgs.push_back("-module-name");
163159
swiftArgs.push_back(clangModuleDep.ID.ModuleName);
164160

161+
// We pass the entire argument list via -Xcc, so the invocation should
162+
// use extra clang options alone.
163+
swiftArgs.push_back("-only-use-extra-clang-opts");
164+
165165
auto pcmPath = moduleCacheRelativeLookupModuleOutput(
166166
clangModuleDep.ID, ModuleOutputKind::ModuleFile,
167167
getModuleCachePathFromClang(getClangInstance()));

lib/DependencyScan/ModuleDependencyCacheSerialization.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -656,19 +656,6 @@ struct llvm::DenseMapInfo<ModuleIdentifierArrayKind> {
656656
}
657657
};
658658

659-
namespace std {
660-
template <>
661-
struct hash<ModuleDependencyID> {
662-
using UnderlyingKindType = std::underlying_type<ModuleDependencyKind>::type;
663-
std::size_t operator()(const ModuleDependencyID &id) const {
664-
auto underlyingKindValue = static_cast<UnderlyingKindType>(id.second);
665-
666-
return (hash<string>()(id.first) ^
667-
(hash<UnderlyingKindType>()(underlyingKindValue)));
668-
}
669-
};
670-
} // namespace std
671-
672659
namespace swift {
673660

674661
class ModuleDependenciesCacheSerializer {

lib/DependencyScan/ScanDependencies.cpp

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "swift/Strings.h"
3737
#include "clang/Basic/Module.h"
3838
#include "llvm/ADT/SetVector.h"
39+
#include "llvm/ADT/SetOperations.h"
3940
#include "llvm/ADT/StringMap.h"
4041
#include "llvm/ADT/StringRef.h"
4142
#include "llvm/ADT/StringSet.h"
@@ -47,6 +48,7 @@
4748
#include <set>
4849
#include <string>
4950
#include <sstream>
51+
#include <algorithm>
5052

5153
using namespace swift;
5254
using namespace swift::dependencies;
@@ -157,6 +159,149 @@ static void findAllImportedClangModules(ASTContext &ctx, StringRef moduleName,
157159
}
158160
}
159161

162+
// Get all dependencies's IDs of this module from its cached
163+
// ModuleDependencyInfo
164+
static ArrayRef<ModuleDependencyID>
165+
getDependencies(const ModuleDependencyID &moduleID,
166+
const ModuleDependenciesCache &cache) {
167+
const auto &optionalModuleInfo =
168+
cache.findDependency(moduleID.first, moduleID.second);
169+
assert(optionalModuleInfo.has_value());
170+
return optionalModuleInfo.value()->getModuleDependencies();
171+
}
172+
173+
/// Implements a topological sort via recursion and reverse postorder DFS.
174+
/// Does not bother handling cycles, relying on a DAG guarantee by the client.
175+
static std::vector<ModuleDependencyID>
176+
computeTopologicalSortOfExplicitDependencies(
177+
const ModuleDependencyIDSetVector &allModules,
178+
const ModuleDependenciesCache &cache) {
179+
std::unordered_set<ModuleDependencyID> visited;
180+
std::vector<ModuleDependencyID> result;
181+
std::stack<ModuleDependencyID> stack;
182+
183+
// Must be explicitly-typed to allow recursion
184+
std::function<void(const ModuleDependencyID &)> visit;
185+
visit = [&visit, &cache, &visited, &result,
186+
&stack](const ModuleDependencyID &moduleID) {
187+
// Mark this node as visited -- we are done if it already was.
188+
if (!visited.insert(moduleID).second)
189+
return;
190+
191+
// Otherwise, visit each adjacent node.
192+
for (const auto &succID : getDependencies(moduleID, cache)) {
193+
// We don't worry if successor is already in this current stack,
194+
// since that would mean we have found a cycle, which should not
195+
// be possible because we checked for cycles earlier.
196+
stack.push(succID);
197+
visit(succID);
198+
auto top = stack.top();
199+
stack.pop();
200+
assert(top == succID);
201+
}
202+
203+
// Add to the result.
204+
result.push_back(moduleID);
205+
};
206+
207+
for (const auto &modID : allModules) {
208+
assert(stack.empty());
209+
stack.push(modID);
210+
visit(modID);
211+
auto top = stack.top();
212+
stack.pop();
213+
assert(top == modID);
214+
}
215+
216+
std::reverse(result.begin(), result.end());
217+
return result;
218+
}
219+
220+
/// For each module in the graph, compute a set of all its dependencies,
221+
/// direct *and* transitive.
222+
static std::unordered_map<ModuleDependencyID,
223+
std::set<ModuleDependencyID>>
224+
computeTransitiveClosureOfExplicitDependencies(
225+
const std::vector<ModuleDependencyID> &topologicallySortedModuleList,
226+
const ModuleDependenciesCache &cache) {
227+
// The usage of an ordered ::set is important to ensure the
228+
// dependencies are listed in a deterministic order.
229+
std::unordered_map<ModuleDependencyID, std::set<ModuleDependencyID>>
230+
result;
231+
for (const auto &modID : topologicallySortedModuleList)
232+
result[modID] = {modID};
233+
234+
// Traverse the set of modules in reverse topological order, assimilating
235+
// transitive closures
236+
for (auto it = topologicallySortedModuleList.rbegin(),
237+
end = topologicallySortedModuleList.rend();
238+
it != end; ++it) {
239+
const auto &modID = *it;
240+
auto &modReachableSet = result[modID];
241+
for (const auto &succID : getDependencies(modID, cache)) {
242+
const auto &succReachableSet = result[succID];
243+
llvm::set_union(modReachableSet, succReachableSet);
244+
}
245+
}
246+
return result;
247+
}
248+
249+
static void
250+
resolveExplicitModuleInputs(ModuleDependencyID moduleID,
251+
const ModuleDependencyInfo &resolvingDepInfo,
252+
const std::set<ModuleDependencyID> &dependencies,
253+
ModuleDependenciesCache &cache) {
254+
auto resolvingInterfaceDepDetails =
255+
resolvingDepInfo.getAsSwiftInterfaceModule();
256+
assert(resolvingInterfaceDepDetails &&
257+
"Expected Swift Interface dependency.");
258+
259+
auto commandLine = resolvingInterfaceDepDetails->buildCommandLine;
260+
for (const auto &depModuleID : dependencies) {
261+
const auto optionalDepInfo =
262+
cache.findDependency(depModuleID.first, depModuleID.second);
263+
assert(optionalDepInfo.has_value());
264+
const auto depInfo = optionalDepInfo.value();
265+
switch (depModuleID.second) {
266+
case swift::ModuleDependencyKind::SwiftInterface: {
267+
auto interfaceDepDetails = depInfo->getAsSwiftInterfaceModule();
268+
assert(interfaceDepDetails && "Expected Swift Interface dependency.");
269+
commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" +
270+
interfaceDepDetails->moduleOutputPath);
271+
} break;
272+
case swift::ModuleDependencyKind::SwiftBinary: {
273+
auto binaryDepDetails = depInfo->getAsSwiftBinaryModule();
274+
assert(binaryDepDetails && "Expected Swift Binary Module dependency.");
275+
commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" +
276+
binaryDepDetails->compiledModulePath);
277+
} break;
278+
case swift::ModuleDependencyKind::SwiftPlaceholder: {
279+
auto placeholderDetails = depInfo->getAsPlaceholderDependencyModule();
280+
assert(placeholderDetails && "Expected Swift Placeholder dependency.");
281+
commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" +
282+
placeholderDetails->compiledModulePath);
283+
} break;
284+
case swift::ModuleDependencyKind::Clang: {
285+
auto clangDepDetails = depInfo->getAsClangModule();
286+
assert(clangDepDetails && "Expected Clang Module dependency.");
287+
commandLine.push_back("-Xcc");
288+
commandLine.push_back("-fmodule-file=" + depModuleID.first + "=" +
289+
clangDepDetails->pcmOutputPath);
290+
commandLine.push_back("-Xcc");
291+
commandLine.push_back("-fmodule-map-file=" +
292+
clangDepDetails->moduleMapFile);
293+
} break;
294+
default:
295+
llvm_unreachable("Unhandled dependency kind.");
296+
}
297+
}
298+
299+
// Update the dependency in the cache with the modified command-line.
300+
auto dependencyInfoCopy = resolvingDepInfo;
301+
dependencyInfoCopy.updateCommandLine(commandLine);
302+
cache.updateDependency(moduleID, dependencyInfoCopy);
303+
}
304+
160305
/// Resolve the direct dependencies of the given module.
161306
static ArrayRef<ModuleDependencyID>
162307
resolveDirectDependencies(CompilerInstance &instance, ModuleDependencyID module,
@@ -1475,8 +1620,29 @@ swift::dependencies::performModuleScan(CompilerInstance &instance,
14751620
if (diagnoseCycle(instance, cache, mainModuleID, ASTDelegate))
14761621
return std::make_error_code(std::errc::not_supported);
14771622

1623+
// Resolve Swift dependency command-line arguments
1624+
// Must happen after cycle detection, because it relies on
1625+
// the DAG property of the dependency graph.
1626+
auto topoSortedModuleList =
1627+
computeTopologicalSortOfExplicitDependencies(allModules, cache);
1628+
auto moduleTransitiveClosures =
1629+
computeTransitiveClosureOfExplicitDependencies(topoSortedModuleList,
1630+
cache);
1631+
for (const auto &dependencyClosure : moduleTransitiveClosures) {
1632+
auto &modID = dependencyClosure.first;
1633+
// For main module or binary modules, no command-line to resolve.
1634+
// For Clang modules, their dependencies are resolved by the clang Scanner
1635+
// itself for us.
1636+
if (modID.second != ModuleDependencyKind::SwiftInterface)
1637+
continue;
1638+
auto optionalDeps = cache.findDependency(modID.first, modID.second);
1639+
assert(optionalDeps.has_value());
1640+
auto deps = optionalDeps.value();
1641+
resolveExplicitModuleInputs(modID, *deps, dependencyClosure.second, cache);
1642+
}
1643+
14781644
auto dependencyGraph = generateFullDependencyGraph(
1479-
instance, cache, ASTDelegate, allModules.getArrayRef());
1645+
instance, cache, ASTDelegate, topoSortedModuleList);
14801646
// Update the dependency tracker.
14811647
if (auto depTracker = instance.getDependencyTracker()) {
14821648
for (auto module : allModules) {

lib/Frontend/CompilerInvocation.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,25 @@ static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts,
13631363
Opts.IncludeClangDocs = false;
13641364
}
13651365

1366+
static bool validateSwiftModuleFileArgumentAndAdd(const std::string &swiftModuleArgument,
1367+
DiagnosticEngine &Diags,
1368+
std::vector<std::pair<std::string, std::string>> &ExplicitSwiftModuleInputs) {
1369+
std::size_t foundDelimeterPos = swiftModuleArgument.find_first_of("=");
1370+
if (foundDelimeterPos == std::string::npos) {
1371+
Diags.diagnose(SourceLoc(), diag::error_swift_module_file_requires_delimeter,
1372+
swiftModuleArgument);
1373+
return true;
1374+
}
1375+
std::string moduleName = swiftModuleArgument.substr(0, foundDelimeterPos),
1376+
modulePath = swiftModuleArgument.substr(foundDelimeterPos+1);
1377+
if (!Lexer::isIdentifier(moduleName)) {
1378+
Diags.diagnose(SourceLoc(), diag::error_bad_module_name, moduleName, false);
1379+
return true;
1380+
}
1381+
ExplicitSwiftModuleInputs.emplace_back(std::make_pair(moduleName, modulePath));
1382+
return false;
1383+
}
1384+
13661385
static bool ParseSearchPathArgs(SearchPathOptions &Opts,
13671386
ArgList &Args,
13681387
DiagnosticEngine &Diags,
@@ -1415,6 +1434,11 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts,
14151434

14161435
if (const Arg *A = Args.getLastArg(OPT_explicit_swift_module_map))
14171436
Opts.ExplicitSwiftModuleMap = A->getValue();
1437+
for (auto A : Args.getAllArgValues(options::OPT_swift_module_file)) {
1438+
if (validateSwiftModuleFileArgumentAndAdd(A, Diags,
1439+
Opts.ExplicitSwiftModuleInputs))
1440+
return true;
1441+
}
14181442
for (auto A: Args.filtered(OPT_candidate_module_file)) {
14191443
Opts.CandidateCompiledModules.push_back(resolveSearchPath(A->getValue()));
14201444
}

lib/Frontend/Frontend.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,10 +566,12 @@ bool CompilerInstance::setUpModuleLoaders() {
566566
bool ExplicitModuleBuild =
567567
Invocation.getFrontendOptions().DisableImplicitModules;
568568
if (ExplicitModuleBuild ||
569-
!Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty()) {
569+
!Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty() ||
570+
!Invocation.getSearchPathOptions().ExplicitSwiftModuleInputs.empty()) {
570571
ESML = ExplicitSwiftModuleLoader::create(
571572
*Context, getDependencyTracker(), MLM,
572573
Invocation.getSearchPathOptions().ExplicitSwiftModuleMap,
574+
Invocation.getSearchPathOptions().ExplicitSwiftModuleInputs,
573575
IgnoreSourceInfoFile);
574576
}
575577

0 commit comments

Comments
 (0)