Swift: Generate nested swift modules in build dir

The nested Swift module structure is recommended for Swift projects.
This structure has an outer directory called
`<Swift_MODULE_NAME>.swiftmodule` that contains generated files for the
interface. The binary swift modules, textual swift interface, and other
supplemental outputs that make up the interface to a Swift library all
go in this outer directory with the Swift module triple as the filename
followed by the appropriate file extension based on what that file
contains.

Issue: #19284
This commit is contained in:
Evan Wilde
2025-04-04 11:03:03 -07:00
parent b36175abdb
commit 1711e86d6c
15 changed files with 110 additions and 15 deletions

View File

@@ -98,6 +98,7 @@ Policies Introduced by CMake 4.1
.. toctree::
:maxdepth: 1
CMP0195: Swift modules in build trees use the Swift module directory structure. </policy/CMP0195>
CMP0194: MSVC is not an assembler for language ASM. </policy/CMP0194>
CMP0193: GNUInstallDirs caches CMAKE_INSTALL_* with leading 'usr/' for install prefix '/'. </policy/CMP0193>
CMP0192: GNUInstallDirs uses absolute SYSCONFDIR, LOCALSTATEDIR, and RUNSTATEDIR in special prefixes. </policy/CMP0192>

27
Help/policy/CMP0195.rst Normal file
View File

@@ -0,0 +1,27 @@
CMP0195
-------
.. versionadded:: 4.1
Swift modules in build trees use the Swift module directory structure.
The Swift compiler emits several supplementary files that make up the
interface to a Swift library. It accepts finding these files separately
or in a single swiftmodule directory. The single file keeps things better
organized and makes it easier to install the resulting products.
CMake versions 4.1 and above prefer to generate the modules in the
directory structure when working with a new enough Swift compiler.
This policy provides compatibility for projects that have not been
updated to expect the new behavior.
The ``OLD`` behavior for this policy is to emit the interface files directly
into the current binary directory.
The ``NEW`` behavior for this policy is to gather the binary swiftmodule and
other supplemental compiler outputs in a single Swift module directory.
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.1
.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
.. include:: include/STANDARD_ADVICE.rst
.. include:: include/DEPRECATED.rst

View File

@@ -6001,8 +6001,15 @@ std::string cmGeneratorTarget::GetSwiftModuleName() const
std::string cmGeneratorTarget::GetSwiftModuleFileName() const
{
return this->GetPropertyOrDefault(
std::string moduleFilename = this->GetPropertyOrDefault(
"Swift_MODULE", this->GetSwiftModuleName() + ".swiftmodule");
if (this->GetPolicyStatusCMP0195() == cmPolicies::NEW) {
if (cmValue moduleTriple =
this->Makefile->GetDefinition("CMAKE_Swift_MODULE_TRIPLE")) {
moduleFilename += "/" + *moduleTriple + ".swiftmodule";
}
}
return moduleFilename;
}
std::string cmGeneratorTarget::GetSwiftModuleDirectory(

View File

@@ -580,7 +580,11 @@ class cmMakefile;
"install prefix '/'.", \
4, 1, 0, WARN) \
SELECT(POLICY, CMP194, "MSVC is not an assembler for language ASM.", 4, 1, \
0, WARN)
0, WARN) \
SELECT( \
POLICY, CMP0195, \
"Swift modules in build trees use the Swift module directory structure.", \
4, 1, 0, WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
@@ -627,7 +631,8 @@ class cmMakefile;
F(CMP0162) \
F(CMP0179) \
F(CMP0181) \
F(CMP0182)
F(CMP0182) \
F(CMP0195)
#define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \
F(CMP0116) \

View File

@@ -0,0 +1 @@
swiftc(.exe)? .* -emit-module -emit-module-path El(/|\\)((Debug|Release)(/|\\))?El\.swiftmodule(/|\\)[-_a-z0-9]+\.swiftmodule -module-name El

View File

@@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 4.0)
cmake_policy(SET CMP0195 NEW)
include(CMP0195-common.cmake)

View File

@@ -0,0 +1 @@
swiftc(.exe)? .* -emit-module -emit-module-path El(/|\\)((Debug|Release)(/|\\))?El.swiftmodule -module-name El

View File

@@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 4.0)
cmake_policy(SET CMP0195 OLD)
include(CMP0195-common.cmake)

View File

@@ -0,0 +1,6 @@
enable_language(Swift)
add_library(L OBJECT L.swift)
set_target_properties(L PROPERTIES
Swift_MODULE_NAME El
Swift_MODULE_DIRECTORY El)

View File

@@ -3,31 +3,32 @@ if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/compile_commands.json")
return()
endif()
set(ESCAPED_BINARY_DIR [==[[^
]*/Tests/RunCMake/Swift/CompileCommands-build]==])
set(E_SOURCE_PATH [==[(\\")?[^
]*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)(\\")?]==])
set(L_SOURCE_PATH [==[(\\")?[^
]*(/Tests/RunCMake/Swift/L.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\L.swift)(\\")?]==])
# The compile command for both files should contain all Swift source files in
# the module
set(expected_compile_commands
[==[^\[
{
"directory": "[^
]*/Tests/RunCMake/Swift/CompileCommands-build",
"directory": "${BINARY_DIR}",
"command": "[^
]*swiftc[^
]* (\\")?[^
]*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)(\\")? (\\")?[^
]*(/Tests/RunCMake/Swift/L.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\L.swift)(\\")?",
]* ${E_SOURCE_PATH} ${L_SOURCE_PATH}",
"file": "[^
]*/Tests/RunCMake/Swift/E.swift",
"output": "[^
]*/CMakeFiles/CompileCommandLib.dir/(Debug/)?E.swift.(o|obj)"
},
{
"directory": "[^
]*/Tests/RunCMake/Swift/CompileCommands-build",
"directory": "${BINARY_DIR}",
"command": "[^
]*swiftc[^
]* (\\")?[^
]*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)(\\")? (\\")?[^
]*(/Tests/RunCMake/Swift/L.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\L.swift)(\\")?",
]* ${E_SOURCE_PATH} ${L_SOURCE_PATH}",
"file": "[^
]*/Tests/RunCMake/Swift/L.swift",
"output": "[^
@@ -35,6 +36,10 @@ set(expected_compile_commands
}]==]
)
string(REPLACE [=[${BINARY_DIR}]=] "${ESCAPED_BINARY_DIR}" expected_compile_commands "${expected_compile_commands}")
string(REPLACE [=[${E_SOURCE_PATH}]=] "${E_SOURCE_PATH}" expected_compile_commands "${expected_compile_commands}")
string(REPLACE [=[${L_SOURCE_PATH}]=] "${L_SOURCE_PATH}" expected_compile_commands "${expected_compile_commands}")
file(READ "${RunCMake_TEST_BINARY_DIR}/compile_commands.json" compile_commands)
if(NOT compile_commands MATCHES "${expected_compile_commands}")
string(REPLACE "\n" "\n " expected_compile_commands_formatted "${expected_compile_commands}")

View File

@@ -1,6 +1,10 @@
if(POLICY CMP0157)
cmake_policy(SET CMP0157 NEW)
endif()
if(POLICY CMP0195)
cmake_policy(SET CMP0195 NEW)
endif()
set(CMAKE_Swift_COMPILATION_MODE "singlefile")
enable_language(Swift)

View File

@@ -1,4 +1,7 @@
cmake_policy(SET CMP0157 NEW)
if(POLICY CMP0195)
cmake_policy(SET CMP0195 NEW)
endif()
enable_language(Swift)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/hello.swift "")

View File

@@ -30,6 +30,21 @@ block()
endif()
endblock()
if(RunCMake_GENERATOR MATCHES "Ninja.*")
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0195-NEW-build)
run_cmake(CMP0195-NEW)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(CMP0195-NEW-build ${CMAKE_COMMAND} --build . -- -n -v)
endblock()
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0195-OLD-build)
run_cmake(CMP0195-OLD)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(CMP0195-OLD-build ${CMAKE_COMMAND} --build . -- -n -v)
endblock()
endif()
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SwiftSimple-build)
run_cmake(SwiftSimple)

View File

@@ -46,6 +46,7 @@
\* CMP0179
\* CMP0181
\* CMP0182
\* CMP0195
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@@ -5,6 +5,10 @@ endif()
if(POLICY CMP0157)
cmake_policy(SET CMP0157 NEW)
endif()
if(POLICY CMP0195)
cmake_policy(SET CMP0195 NEW)
set(Swift_NESTED_MODULE TRUE)
endif()
# NOTE: Force the Release mode configuration as there are some issues with the
# debug information handling on macOS on certain Xcode builds.
@@ -52,11 +56,18 @@ target_link_libraries(N PUBLIC
# FIXME(#25989): The Xcode generator doesn't respect CMAKE_Swift_MODULE_DIRECTORY.
if(NOT CMAKE_GENERATOR STREQUAL "Xcode")
if(Swift_NESTED_MODULE)
# Swift module is a directory, grab the binary swiftmodule file
set(ModulePath "${CMAKE_Swift_MODULE_DIRECTORY}/M.swiftmodule/${CMAKE_Swift_MODULE_TRIPLE}.swiftmodule")
else()
# Swift modules are files
set(ModulePath "${CMAKE_Swift_MODULE_DIRECTORY}/M.swiftmodule")
endif()
add_custom_command(TARGET M
POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E compare_files
"${CMAKE_Swift_MODULE_DIRECTORY}/M.swiftmodule"
"${CMAKE_Swift_MODULE_DIRECTORY}/M.swiftmodule"
"${ModulePath}"
"${ModulePath}"
COMMENT "check that .swiftmodule files are generated in CMAKE_Swift_MODULE_DIRECTORY"
VERBATIM)
endif()