From 630dd51e43c806cba2199477d17db7e3abc06c6b Mon Sep 17 00:00:00 2001 From: Fabrice de Gans Date: Wed, 22 Apr 2026 18:25:20 +0200 Subject: [PATCH] CMake: Support CMP0157 when available CMP0157 (introduced in CMake 3.29) improves Swift support in CMake by handling many build details natively instead of requiring manual configuration. It also introduces an abstraction for selecting the Swift compilation mode per target. Supporting CMP0157 will simplify the Swift toolchain build once the necessary changes have landed in the main Swift repository. These changes make it possible to support CMP0157 set to either OLD or NEW. We cannot yet require CMP0157 to be NEW, because the Swift compiler build (which consumes swift-syntax via FetchContent) has not yet adopted the policy. When the parent project's policy is OLD, swift-syntax falls back to the legacy code path. The OLD support should be removed once the Swift-repo-side changes have landed. --- CMakeLists.txt | 23 ++++- cmake/modules/AddSwiftHostLibrary.cmake | 125 +++++++++++++----------- 2 files changed, 87 insertions(+), 61 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff669fbfb89..333dc8c62f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,25 @@ cmake_minimum_required(VERSION 3.19.6) +if(POLICY CMP0157) + cmake_policy(SET CMP0157 NEW) +endif() + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) project(SwiftSyntax LANGUAGES C Swift) +if(CMAKE_Swift_COMPILATION_MODE_DEFAULT) + # Modern way of doing split builds with CMake 3.29+. This allows us to get proper module + # dependencies and avoid the workaround in AddSwiftHostLibrary.cmake. + set(SWIFT_SYNTAX_USE_SPLIT_BUILD TRUE) +else() + # Legacy way of doing split builds, which is to build the module in a separate target and then + # link to it. This is necessary for CMake versions before 3.29, which don't have proper support + # for Swift module dependencies. + set(SWIFT_SYNTAX_USE_SPLIT_BUILD FALSE) +endif() + set(SWIFT_VERSION 5) set(CMAKE_Swift_LANGUAGE_VERSION ${SWIFT_VERSION}) @@ -37,9 +52,11 @@ endif() set(CMAKE_MACOSX_RPATH YES) -option(SWIFT_SYNTAX_ENABLE_WMO_PRE_3_26 - "Enable Whole Module Optimization (WMO) - requires swift-driver" - $>,$>,YES,NO>) +if(NOT SWIFT_SYNTAX_USE_SPLIT_BUILD) + option(SWIFT_SYNTAX_ENABLE_WMO_PRE_3_26 + "Enable Whole Module Optimization (WMO) - requires swift-driver" + $>,$>,YES,NO>) +endif() include(AddSwiftHostLibrary) include(SwiftCompilerCapability) diff --git a/cmake/modules/AddSwiftHostLibrary.cmake b/cmake/modules/AddSwiftHostLibrary.cmake index 4c4b6e0b977..b6a0e46b3e5 100644 --- a/cmake/modules/AddSwiftHostLibrary.cmake +++ b/cmake/modules/AddSwiftHostLibrary.cmake @@ -26,26 +26,28 @@ function(target_link_swift_syntax_libraries TARGET) target_link_libraries(${TARGET} ${link_type} ${dependencies}) - # cmake generation for Swift adds an order only dependency, which matches how C-family languages - # works. In that case, however, ninja (and presumably other generators) will rebuild on header - # changes. That's not the case for Swift, and thus if A -> B, A is not being rebuilt when the - # ABI/API of B changes. - # - # For now workaround this by touching a file whenever B is rebuilt and then compiling that file as - # part of A. Ideally this file would be generated by each of the targets, but that dependency didn't - # seem to be being tracked. - # - # Remove once rdar://102202478 is fixed. - foreach(DEPENDENCY ${dependencies}) - string(REGEX REPLACE [<>:\"/\\|?*] _ sanitized ${DEPENDENCY}) - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift - COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift - DEPENDS ${DEPENDENCY} - ) - target_sources(${TARGET} PRIVATE - ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift - ) - endforeach() + if(NOT SWIFT_SYNTAX_USE_SPLIT_BUILD) + # cmake generation for Swift adds an order only dependency, which matches how C-family languages + # work. In that case, however, ninja (and presumably other generators) will rebuild on header + # changes. That's not the case for Swift, and thus if A -> B, A is not being rebuilt when the + # ABI/API of B changes. + # + # For now workaround this by touching a file whenever B is rebuilt and then compiling that file as + # part of A. Ideally this file would be generated by each of the targets, but that dependency didn't + # seem to be being tracked. + # + # Remove once rdar://102202478 is fixed. + foreach(DEPENDENCY ${dependencies}) + string(REGEX REPLACE [=[[<>:"/\|?*]]=] _ sanitized ${DEPENDENCY}) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift + DEPENDS ${DEPENDENCY} + ) + target_sources(${TARGET} PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift + ) + endforeach() + endif() endfunction() # Add a new host library with the given name. @@ -67,14 +69,6 @@ function(add_swift_syntax_library name) set(module_file "${module_base}/${SWIFT_HOST_MODULE_TRIPLE}.swiftmodule") set(module_interface_file "${module_base}/${SWIFT_HOST_MODULE_TRIPLE}.swiftinterface") set(module_private_interface_file "${module_base}/${SWIFT_HOST_MODULE_TRIPLE}.private.swiftinterface") - set(module_sourceinfo_file "${module_base}/${SWIFT_HOST_MODULE_TRIPLE}.swiftsourceinfo") - - # Add a custom target to create the module directory. - add_custom_command( - TARGET ${target} - PRE_BUILD - COMMAND "${CMAKE_COMMAND}" -E make_directory ${module_base} - COMMENT "Generating module directory for ${target}") # Configure the emission of the Swift module files. target_compile_options("${target}" PRIVATE @@ -82,7 +76,6 @@ function(add_swift_syntax_library name) -DRESILIENT_LIBRARIES; -enable-library-evolution; -emit-module-path;${module_file}; - -emit-module-source-info-path;${module_sourceinfo_file}; -emit-module-interface-path;${module_interface_file}; -emit-private-module-interface-path;${module_private_interface_file} >) @@ -106,43 +99,64 @@ function(add_swift_syntax_library name) else() set(module_dir ${CMAKE_CURRENT_BINARY_DIR}) set(module_base "${module_dir}/${name}.swiftmodule") - set(module_file "${module_file}") endif() - # Touch the library and objects to workaround their mtime not being updated - # when there are no real changes (eg. a file was updated with a comment). - # Ideally this should be done in the driver, which could only update the - # files that have changed. - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ "${module_base}" - COMMAND_EXPAND_LISTS - COMMENT "Update mtime of library outputs workaround") - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ - COMMAND_EXPAND_LISTS - COMMENT "Update mtime of objcect files workaround") - set_target_properties(${target} PROPERTIES Swift_MODULE_NAME ${name} Swift_MODULE_DIRECTORY ${module_dir} INTERFACE_INCLUDE_DIRECTORIES ${module_dir} ) - # Configure the emission of the Swift module files. target_compile_options("${target}" PRIVATE $<$: - "SHELL:-module-name ${name}" "SHELL:-Xfrontend -module-abi-name -Xfrontend ${SWIFT_MODULE_ABI_NAME_PREFIX}${name}" - "-no-emit-module-separately-wmo" + >) + + if(NOT SWIFT_SYNTAX_USE_SPLIT_BUILD) + # Legacy build configuration. + if(SWIFTSYNTAX_EMIT_MODULE) + set(module_sourceinfo_file "${module_base}/${SWIFT_HOST_MODULE_TRIPLE}.swiftsourceinfo") + + # Add a custom target to create the module directory. + add_custom_command( + TARGET ${target} + PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory ${module_base} + COMMENT "Generating module directory for ${target}") + + # Configure the emission of the Swift module files. + target_compile_options("${target}" PRIVATE + $<$: + -emit-module-source-info-path;${module_sourceinfo_file}; + >) + endif() + + # Touch the library and objects to workaround their mtime not being updated + # when there are no real changes (eg. a file was updated with a comment). + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ "${module_base}" + COMMAND_EXPAND_LISTS + COMMENT "Update mtime of library outputs workaround") + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ + COMMAND_EXPAND_LISTS + COMMENT "Update mtime of object files workaround") + + # Configure the emission of the Swift module files. + target_compile_options("${target}" PRIVATE + $<$: + "SHELL:-module-name ${name}" + "-no-emit-module-separately-wmo" >) - if(CMAKE_VERSION VERSION_LESS 3.26.0 AND SWIFT_SYNTAX_ENABLE_WMO_PRE_3_26) - target_compile_options(${target} PRIVATE - $<$:-wmo>) + if(CMAKE_VERSION VERSION_LESS 3.26.0 AND SWIFT_SYNTAX_ENABLE_WMO_PRE_3_26) + target_compile_options(${target} PRIVATE + $<$:-wmo>) + endif() endif() target_compile_options(${target} PRIVATE @@ -155,11 +169,6 @@ function(add_swift_syntax_library name) ) endif() - # NOTE: workaround for CMake not setting up include flags yet - set_target_properties(${target} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${module_dir} - ) - set_target_properties(${target} PROPERTIES BUILD_WITH_INSTALL_RPATH YES )