diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index c9ce1df0a6..c962997e8a 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -1652,6 +1652,14 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( } } + // For split Swift builds, ensure the link edge depends on the target's own + // .swiftmodule so the emit-module edge runs even when no other target in + // the build depends on it (e.g. install-only targets). + std::string swiftModuleOutput = this->GetSwiftModuleOutput(config); + if (!swiftModuleOutput.empty()) { + linkBuild.ImplicitDeps.emplace_back(std::move(swiftModuleOutput)); + } + // Ninja should restat after linking if and only if there are byproducts. vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1"; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 12ecbee94e..2469d9fb17 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -2202,6 +2202,9 @@ void cmNinjaTargetGenerator::WriteSwiftObjectBuildStatement( // Importable targets keep -emit-module on compile so swiftc still emits // .swiftdoc. When splitting module emission, both the .swiftmodule output // and -emit-module flags move entirely to the separate emit-module edge. + if (targetIsImportable) { + this->Configs[config].SwiftModuleOutput = moduleFilepath; + } if (targetIsImportable && !emitModuleSeparately) { objBuild.Outputs.push_back(moduleFilepath); } @@ -2673,6 +2676,16 @@ cmNinjaDeps cmNinjaTargetGenerator::GetObjects(std::string const& config) const return {}; } +std::string cmNinjaTargetGenerator::GetSwiftModuleOutput( + std::string const& config) const +{ + auto const it = this->Configs.find(config); + if (it != this->Configs.end()) { + return it->second.SwiftModuleOutput; + } + return {}; +} + void cmNinjaTargetGenerator::EnsureDirectoryExists( std::string const& path) const { diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index c706e75958..41f3f7c908 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -216,6 +216,7 @@ protected: void AdditionalCleanFiles(std::string const& config); cmNinjaDeps GetObjects(std::string const& config) const; + std::string GetSwiftModuleOutput(std::string const& config) const; void EnsureDirectoryExists(std::string const& dir) const; void EnsureParentDirectoryExists(std::string const& path) const; @@ -278,6 +279,8 @@ private: Json::Value SwiftOutputMap; cmNinjaDeps ExtraFiles; std::unique_ptr MacOSXContentGenerator; + // Path declared as the .swiftmodule output (compile or emit-module edge). + std::string SwiftModuleOutput; }; std::map Configs; diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake new file mode 100644 index 0000000000..90038eacc1 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake @@ -0,0 +1,14 @@ +# The link edge for a Swift library must implicitly depend on the +# .swiftmodule so the emit-module edge runs even when no other target +# in the build depends on it. +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +if(NOT build_ninja MATCHES "build [^\n]*(libL\\.a|L\\.lib)[^\n]*:.*\\|[^\n]*L\\.swiftmodule") + string(APPEND RunCMake_TEST_FAILED + "Link edge for L does not depend on L.swiftmodule.\n") +endif() diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake new file mode 100644 index 0000000000..7ef69cfcc9 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake @@ -0,0 +1,12 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# A standalone library with no dependent Swift targets. The link edge +# must still depend on the .swiftmodule so the emit-module edge runs. +add_library(L STATIC L.swift) diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake new file mode 100644 index 0000000000..2aca41d385 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake @@ -0,0 +1,14 @@ +# Same as EmitModuleSeparatelyLinkDep but with a custom module directory. +# The link edge must depend on the actual emit-module output path, not a +# default that doesn't match the target's configuration. +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +if(NOT build_ninja MATCHES "build [^\n]*(libL\\.a|L\\.lib)[^\n]*:.*\\|[^\n]*custom(/|\\\\)[^\n]*L\\.swiftmodule") + string(APPEND RunCMake_TEST_FAILED + "Link edge for L does not depend on custom/.../L.swiftmodule.\n") +endif() diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake new file mode 100644 index 0000000000..fbb3ac86f2 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake @@ -0,0 +1,15 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# A standalone library with a custom module output path (like swiftCore +# in the Swift stdlib). The link edge must depend on the actual +# emit-module output, not GetSwiftModulePath(). +add_library(L STATIC L.swift) +set_target_properties(L PROPERTIES + Swift_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/custom) diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake index 2bfbc30ca0..7920c9f2c7 100644 --- a/Tests/RunCMake/Swift/RunCMakeTest.cmake +++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake @@ -170,6 +170,14 @@ if(RunCMake_GENERATOR MATCHES "Ninja") run_cmake_command(EmitModuleSeparatelyDirectoryStyle-build ${CMAKE_COMMAND} --build . -- -vn) endblock() + block() + run_cmake(EmitModuleSeparatelyLinkDep) + endblock() + + block() + run_cmake(EmitModuleSeparatelyLinkDepCustomPath) + endblock() + block() set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EmitModuleSeparatelyExistingModulePath-build) run_cmake(EmitModuleSeparatelyExistingModulePath)