Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions include/swift/Frontend/ModuleInterfaceLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,19 +173,24 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase {
~ExplicitSwiftModuleLoader();
};

/// Information about explicitly specified Swift module files.
/// Information about explicitly specified Swift and Clang module files.
struct ExplicitModuleInfo {
// Path of the .swiftmodule file.
// Path of the .swiftmodule file. Empty for pure Clang modules.
std::string modulePath;
// Path of the .swiftmoduledoc file.
// Path of the .swiftmoduledoc file. Empty for pure Clang modules.
std::string moduleDocPath;
// Path of the .swiftsourceinfo file.
// Path of the .swiftsourceinfo file. Empty for pure Clang modules.
std::string moduleSourceInfoPath;
// A flag that indicates whether this module is a framework
bool isFramework = false;
// A flag that indicates whether this module is a system module
// Set the default to be false.
bool isSystem = false;
// Path of the Clang module map file. Empty for pure Swift modules.
std::string clangModuleMapPath;
// Path of a compiled Clang explicit module file. Empty for pure Swift
// modules.
std::string clangModulePath;
};

/// Parser of explicit module maps passed into the compiler.
Expand All @@ -194,15 +199,19 @@ struct ExplicitModuleInfo {
// "moduleName": "A",
// "modulePath": "A.swiftmodule",
// "docPath": "A.swiftdoc",
// "sourceInfoPath": "A.swiftsourceinfo"
// "isFramework": false
// "sourceInfoPath": "A.swiftsourceinfo",
// "isFramework": false,
// "clangModuleMapPath": "A/module.modulemap",
// "clangModulePath": "A.pcm",
// },
// {
// "moduleName": "B",
// "modulePath": "B.swiftmodule",
// "docPath": "B.swiftdoc",
// "sourceInfoPath": "B.swiftsourceinfo"
// "isFramework": false
// "sourceInfoPath": "B.swiftsourceinfo",
// "isFramework": false,
// "clangModuleMapPath": "B/module.modulemap",
// "clangModulePath": "B.pcm",
// }
// ]
class ExplicitModuleMapParser {
Expand Down Expand Up @@ -279,6 +288,10 @@ class ExplicitModuleMapParser {
result.isFramework = parseBoolValue(val);
} else if (key == "isSystem") {
result.isSystem = parseBoolValue(val);
} else if (key == "clangModuleMapPath") {
result.clangModuleMapPath = val.str();
} else if (key == "clangModulePath") {
result.clangModulePath = val.str();
} else {
// Being forgiving for future fields.
continue;
Expand Down
26 changes: 17 additions & 9 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,20 @@ bool CompilerInstance::setUpModuleLoaders() {
Context->addModuleLoader(std::move(MemoryBufferLoader));
}

// If using `-explicit-swift-module-map-file`, create the explicit loader
// before creating `ClangImporter` because the entries in the map influence
// the Clang flags. The loader is added to the context below.
std::unique_ptr<ExplicitSwiftModuleLoader> ESML = nullptr;
bool ExplicitModuleBuild =
Invocation.getFrontendOptions().DisableImplicitModules;
if (ExplicitModuleBuild ||
!Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty()) {
ESML = ExplicitSwiftModuleLoader::create(
*Context, getDependencyTracker(), MLM,
Invocation.getSearchPathOptions().ExplicitSwiftModuleMap,
IgnoreSourceInfoFile);
}

// Wire up the Clang importer. If the user has specified an SDK, use it.
// Otherwise, we just keep it around as our interface to Clang's ABI
// knowledge.
Expand All @@ -575,15 +589,9 @@ bool CompilerInstance::setUpModuleLoaders() {
*Context, ModuleCachePath, FEOpts.PrebuiltModuleCachePath,
FEOpts.BackupModuleInterfaceDir, LoaderOpts,
RequireOSSAModules_t(Invocation.getSILOptions())));
// If implicit modules are disabled, we need to install an explicit module
// loader.
bool ExplicitModuleBuild = Invocation.getFrontendOptions().DisableImplicitModules;
if (ExplicitModuleBuild || !Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty()) {
auto ESML = ExplicitSwiftModuleLoader::create(
*Context,
getDependencyTracker(), MLM,
Invocation.getSearchPathOptions().ExplicitSwiftModuleMap,
IgnoreSourceInfoFile);

// Install an explicit module loader if it was created earlier.
if (ESML) {
this->DefaultSerializedLoader = ESML.get();
Context->addModuleLoader(std::move(ESML));
}
Expand Down
31 changes: 31 additions & 0 deletions lib/Frontend/ModuleInterfaceLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1875,6 +1875,27 @@ struct ExplicitSwiftModuleLoader::Implementation {
else if (result == std::errc::no_such_file_or_directory)
Ctx.Diags.diagnose(SourceLoc(), diag::explicit_swift_module_map_missing,
fileName);

// A single module map can define multiple modules; keep track of the ones
// we've seen so that we don't generate duplicate flags.
std::set<std::string> moduleMapsSeen;
std::vector<std::string> &extraClangArgs = Ctx.ClangImporterOpts.ExtraArgs;
for (auto &entry : ExplicitModuleMap) {
const auto &moduleMapPath = entry.getValue().clangModuleMapPath;
if (!moduleMapPath.empty() &&
moduleMapsSeen.find(moduleMapPath) == moduleMapsSeen.end()) {
moduleMapsSeen.insert(moduleMapPath);
extraClangArgs.push_back(
(Twine("-fmodule-map-file=") + moduleMapPath).str());
}

const auto &modulePath = entry.getValue().clangModulePath;
if (!modulePath.empty()) {
extraClangArgs.push_back(
(Twine("-fmodule-file=") + entry.getKey() + "=" + modulePath)
.str());
}
}
}
};

Expand Down Expand Up @@ -1910,6 +1931,16 @@ bool ExplicitSwiftModuleLoader::findModule(ImportPath::Element ModuleID,
}
auto &moduleInfo = it->getValue();

// If this is only a Clang module with no paired Swift module, return false
// now so that we don't emit diagnostics about it being missing. This gives
// ClangImporter an opportunity to import it.
bool hasClangModule = !moduleInfo.clangModuleMapPath.empty() ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an &&? If the PCM path is not specified, explicit load of this module will fail.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, -explicit-swift-module-map-file doesn't always have to imply explicit Clang modules. The test case explicit-module-map-clang-implicit.swift shows that passing only the module map path works, so the implicit Clang module cache is being used there.

!moduleInfo.clangModulePath.empty();
bool hasSwiftModule = !moduleInfo.modulePath.empty();
if (hasClangModule && !hasSwiftModule) {
return false;
}

// Set IsFramework bit according to the moduleInfo
IsFramework = moduleInfo.isFramework;
IsSystemModule = moduleInfo.isSystem;
Expand Down
43 changes: 43 additions & 0 deletions test/ScanDependencies/explicit-module-map-clang-and-swift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %empty-directory(%t)
// RUN: mkdir -p %t/clang-module-cache
// RUN: mkdir -p %t/inputs
// RUN: echo "public func anotherFuncA() {}" > %t/A.swift
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/inputs/A.swiftmodule -emit-module-doc-path %t/inputs/A.swiftdoc -emit-module-source-info -emit-module-source-info-path %t/inputs/A.swiftsourceinfo -import-underlying-module -I%S/Inputs/CHeaders -module-cache-path %t.module-cache %t/A.swift -module-name A

// RUN: echo "[{" > %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"A\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/t/inputs/A.swiftmodule\"," >> %/t/inputs/map.json
// RUN: echo "\"docPath\": \"%/t/inputs/A.swiftdoc\"," >> %/t/inputs/map.json
// RUN: echo "\"sourceInfoPath\": \"%/t/inputs/A.swiftsourceinfo\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
// RUN: echo "\"clangModuleMapPath\": \"%/S/Inputs/CHeaders/module.modulemap\"" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}]" >> %/t/inputs/map.json

// RUN: %target-swift-frontend -emit-module -emit-module-path %t/Foo.swiftmodule -module-cache-path %t.module-cache -explicit-swift-module-map-file %t/inputs/map.json %s

import A

func callA() {
funcA()
anotherFuncA()
}
39 changes: 39 additions & 0 deletions test/ScanDependencies/explicit-module-map-clang-explicit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %empty-directory(%t)
// RUN: mkdir -p %t/clang-module-cache
// RUN: mkdir -p %t/inputs

// RUN: echo "[{" > %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"A\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
// RUN: echo "\"clangModuleMapPath\": \"%/S/Inputs/CHeaders/module.modulemap\"," >> %/t/inputs/map.json
// RUN: echo "\"clangModulePath\": \"%t/inputs/A.pcm\"" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}]" >> %/t/inputs/map.json

// RUN: %target-swift-emit-pcm -module-name A -o %t/inputs/A.pcm %S/Inputs/CHeaders/module.modulemap
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/Foo.swiftmodule -module-cache-path %t.module-cache -explicit-swift-module-map-file %t/inputs/map.json %s

import A

func callA() {
funcA()
}
37 changes: 37 additions & 0 deletions test/ScanDependencies/explicit-module-map-clang-implicit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %empty-directory(%t)
// RUN: mkdir -p %t/clang-module-cache
// RUN: mkdir -p %t/inputs

// RUN: echo "[{" > %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"A\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
// RUN: echo "\"clangModuleMapPath\": \"%/S/Inputs/CHeaders/module.modulemap\"" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}," >> %/t/inputs/map.json
// RUN: echo "{" >> %/t/inputs/map.json
// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json
// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
// RUN: echo "}]" >> %/t/inputs/map.json

// RUN: %target-swift-frontend -emit-module -emit-module-path %t/Foo.swiftmodule -module-cache-path %t.module-cache -explicit-swift-module-map-file %t/inputs/map.json %s

import A

func callA() {
funcA()
}