From 4165eb3d0b21601977b26e8f8af5193c55169cee Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Fri, 30 Dec 2022 23:41:16 -0800 Subject: Ninja: Emit swiftmodule from executable with exports This patch adds support for tracking the swiftmodules for executables exporting symbols. This fixes a bug in the earlier implementation around emitting the swiftmodule. Previously, the code would use `CMAKE_EXE_EXPORTS_Swift_FLAG` to inject the `-emit-module`, and module path information into the `CMAKE_Swift_LINK_EXECUTABLE` rule. Because Swift skips the build step and only runs during the link phase, these flags were injected in `cmNinjaNormalTargetGenerator::ComputeLinkCmd` instead of `cmLocalGenerator::GetTargetFlags` where it is done normally. Unfortunately, injecting in `ComputeLinkCmd` didn't do anything because we have a `linkCmd` so `ComputeLinkCmd` exits early, before the EXE_EXPORT flags get added to the link command. Instead of playing with that flag, CMake checks `CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS` and uses that as the link rule if it exists and falls back on `CMAKE_Swift_LINK_EXECUTABLE`. I've defined that variable in terms of `CMAKE_Swift_LINK_EXECUTABLE` with the necessary additional flags for emitting the swift module instead. This has the same end effect as the desired behavior, but simplifies things a bit. Since we're generating the swiftmodule for executables with exports, I've also updated the dependency graph to include the swiftmodule as an output in the build dependency graph if the executable has exports. Tests updated: - RunCMake/NoWorkToDo: Ensure that the build graph does not result in unnecessary rebuilds with exporting executables. - SwiftOnly: Ensure that we can consume functions defined in the executable by a library getting linked into said executable. --- Modules/CMakeSwiftInformation.cmake | 6 ++++-- Source/cmNinjaNormalTargetGenerator.cxx | 10 ++-------- Tests/RunCMake/Swift/NoWorkToDo.cmake | 5 ++++- Tests/SwiftOnly/CMakeLists.txt | 11 +++++++++++ Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt | 5 +++++ Tests/SwiftOnly/SwiftPlugin/main.swift | 4 ++++ Tests/SwiftOnly/SwiftPlugin/plugin.swift | 3 +++ 7 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt create mode 100644 Tests/SwiftOnly/SwiftPlugin/main.swift create mode 100644 Tests/SwiftOnly/SwiftPlugin/plugin.swift diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index 62f7ef2..a4b8ccc 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake @@ -17,8 +17,6 @@ if(CMAKE_Swift_COMPILER_ID) include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_Swift_COMPILER_ID}-Swift OPTIONAL) endif() -set(CMAKE_EXE_EXPORTS_Swift_FLAG "-emit-module -emit-module-path ${CMAKE_Swift_IMPLIB_LINKER_FLAGS}") - set(CMAKE_INCLUDE_FLAG_Swift "-I ") if(CMAKE_SYSTEM_NAME STREQUAL Darwin) set(CMAKE_SHARED_LIBRARY_SONAME_Swift_FLAG "-Xlinker -install_name -Xlinker ") @@ -114,6 +112,10 @@ if(NOT CMAKE_Swift_LINK_EXECUTABLE) set(CMAKE_Swift_LINK_EXECUTABLE " -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o -emit-dependencies ") endif() +if(NOT CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS) + set(CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS "${CMAKE_Swift_LINK_EXECUTABLE} -emit-module -emit-module-path ${CMAKE_Swift_IMPLIB_LINKER_FLAGS}") +endif() + if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY) set(CMAKE_Swift_CREATE_STATIC_LIBRARY " -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o -module-name -module-link-name -emit-module -emit-module-path -emit-dependencies ") diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 6ec1781..84cff2e 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -644,14 +644,7 @@ std::vector cmNinjaNormalTargetGenerator::ComputeLinkCmd( } break; case cmStateEnums::SHARED_LIBRARY: case cmStateEnums::MODULE_LIBRARY: - break; case cmStateEnums::EXECUTABLE: - if (this->TargetLinkLanguage(config) == "Swift") { - if (this->GeneratorTarget->IsExecutableWithExports()) { - this->Makefile->GetDefExpandList("CMAKE_EXE_EXPORTS_Swift_FLAG", - linkCmds); - } - } break; default: assert(false && "Unexpected target type"); @@ -1112,7 +1105,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( this->GetObjectFilePath(source, config)); } } - if (targetType != cmStateEnums::EXECUTABLE) { + if (targetType != cmStateEnums::EXECUTABLE || + gt->IsExecutableWithExports()) { linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]); } } else { diff --git a/Tests/RunCMake/Swift/NoWorkToDo.cmake b/Tests/RunCMake/Swift/NoWorkToDo.cmake index e86a861..51c2ff3 100644 --- a/Tests/RunCMake/Swift/NoWorkToDo.cmake +++ b/Tests/RunCMake/Swift/NoWorkToDo.cmake @@ -1,2 +1,5 @@ enable_language(Swift) -add_executable(hello hello.swift) +add_executable(hello1 hello.swift) +set_target_properties(hello1 PROPERTIES ENABLE_EXPORTS TRUE) + +add_executable(hello2 hello.swift) diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt index fa8687d..13cf2b1 100644 --- a/Tests/SwiftOnly/CMakeLists.txt +++ b/Tests/SwiftOnly/CMakeLists.txt @@ -43,3 +43,14 @@ target_link_libraries(N PUBLIC # Dummy to make sure generation works with such targets. add_library(SwiftIface INTERFACE) target_link_libraries(SwiftOnly PRIVATE SwiftIface) + +# @_alwaysEmitIntoClient ensures that the function body is inserted into the +# swiftmodule instead of as a symbol in the binary itself. I'm doing this to +# avoid having to link the executable. There are some flags required in order to +# link an executable into a library that I didn't see CMake emitting for Swift +# on macOS. AEIC is the easiest workaround that still tests this functionality. +# Unfortunately, AEIC was only added recently (~Swift 5.2), so we need to check +# that it is available before using it. +if(CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 5.2) + add_subdirectory("SwiftPlugin") +endif() diff --git a/Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt b/Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt new file mode 100644 index 0000000..4069f16 --- /dev/null +++ b/Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(main main.swift) +set_target_properties(main PROPERTIES ENABLE_EXPORTS TRUE) + +add_library(plugin plugin.swift) +target_link_libraries(plugin PRIVATE main) diff --git a/Tests/SwiftOnly/SwiftPlugin/main.swift b/Tests/SwiftOnly/SwiftPlugin/main.swift new file mode 100644 index 0000000..f5aac51 --- /dev/null +++ b/Tests/SwiftOnly/SwiftPlugin/main.swift @@ -0,0 +1,4 @@ +@_alwaysEmitIntoClient +public func exported() -> Int { 32 } + +print(exported()) diff --git a/Tests/SwiftOnly/SwiftPlugin/plugin.swift b/Tests/SwiftOnly/SwiftPlugin/plugin.swift new file mode 100644 index 0000000..e84f248 --- /dev/null +++ b/Tests/SwiftOnly/SwiftPlugin/plugin.swift @@ -0,0 +1,3 @@ +import main + +public func importing() -> Int { main.exported() + 1 } -- cgit v0.12