CMake multi-module shared library build does not build with -Wl,-z,defs and -Wl,--no-undefined,--no-allow-shlib-undefined on Linux
I'm trying to start building my project with all of -z,defs, --no-undefined and --no-allow-shlib-undefined on ELF platforms.
I'm pulling in swift-collections via FetchContent.
libDequeModule.so fails to link with an "undefined symbol: ceil" error when linking with lld from a main-snapshot.
Information
- Package version: 1.1.2
- Platform version: Ubuntu 24.04
- Swift version: Swift version 6.2-dev (LLVM 1b38440e27d7b93, Swift 0a9ab413a091180)
- Swiftly list output: main-snapshot-2024-12-16 (in use)
Checklist
- [x] If possible, I've reproduced the issue using the
mainbranch of this package. - [x] I've searched for existing GitHub issues.
Steps to Reproduce
Create a directory with the following CMakeLists.txt
CMakeLists.txt
cmake_minimum_required(VERSION 3.31)
project(example LANGUAGES Swift CXX)
macro(add_cxx_compile_options)
set(args "")
foreach(arg ${ARGN})
string(APPEND args ${arg}$<SEMICOLON>)
add_compile_options("SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc ${arg}>")
endforeach()
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX,ASM>:${args}>)
endmacro()
set(BUILD_SHARED_LIBS ON)
if (NOT WIN32 AND NOT APPLE)
# NOTE: Assume ELF
add_link_options(LINKER:-z,defs)
add_link_options(LINKER:--no-undefined)
add_link_options(LINKER:--no-allow-shlib-undefined)
endif()
include(FetchContent)
set(FETCHCONTENT_TRY_FIND_PACKAGE_MODE OPT_IN)
FetchContent_Declare(SwiftCollections
GIT_REPOSITORY https://github.com/apple/swift-collections.git
GIT_TAG main
OVERRIDE_FIND_PACKAGE
)
FetchContent_MakeAvailable(SwiftCollections)
Generate a build:
cmake -Bbuild -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
Run the build:
ninja -C build
Expected behavior
Successful build of all the swift-collections modules
Actual behavior
andrew@Andrew-Workstation:~/ladybird-org/testing/swift-tests/swift-collections-no-undefined$ cmake -Bbuild -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
-- The Swift compiler identification is Apple 6.2
-- The CXX compiler identification is Clang 17.0.0
-- Check for working Swift compiler: /home/andrew/.local/bin/swiftc
-- Check for working Swift compiler: /home/andrew/.local/bin/swiftc - works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /home/andrew/.local/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
Cloning into 'swiftcollections-src'...
Already on 'main'
Your branch is up to date with 'origin/main'.
-- The C compiler identification is Clang 17.0.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/andrew/.local/bin/clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done (2.9s)
-- Generating done (0.0s)
-- Build files have been written to: /home/andrew/ladybird-org/testing/swift-tests/swift-collections-no-undefined/build
andrew@Andrew-Workstation:~/ladybird-org/testing/swift-tests/swift-collections-no-undefined$ ninja -C build/
ninja: Entering directory `build/'
[6/16] Building Swift Module 'DequeModule' with 17 sources
/home/andrew/ladybird-org/testing/swift-tests/swift-collections-no-undefined/build/_deps/swiftcollections-src/Sources/DequeModule/Deque+Collection.swift:26:18: warning: stored property '_storage' of 'Sendable'-conforming struct 'Iterator' has non-sendable type 'Deque<Element>._Storage'; this is an error in the Swift 6 language mode
24 | public struct Iterator: IteratorProtocol {
25 | @usableFromInline
26 | internal var _storage: Deque._Storage
| `- warning: stored property '_storage' of 'Sendable'-conforming struct 'Iterator' has non-sendable type 'Deque<Element>._Storage'; this is an error in the Swift 6 language mode
27 |
28 | @usableFromInline
/home/andrew/ladybird-org/testing/swift-tests/swift-collections-no-undefined/build/_deps/swiftcollections-src/Sources/DequeModule/Deque._Storage.swift:15:10: note: consider making struct '_Storage' conform to the 'Sendable' protocol
13 | @frozen
14 | @usableFromInline
15 | struct _Storage {
| `- note: consider making struct '_Storage' conform to the 'Sendable' protocol
16 | @usableFromInline
17 | internal typealias _Buffer = ManagedBufferPointer<_DequeBufferHeader, Element>
/home/andrew/ladybird-org/testing/swift-tests/swift-collections-no-undefined/build/_deps/swiftcollections-src/Sources/DequeModule/Deque+Collection.swift:26:18: warning: stored property '_storage' of 'Sendable'-conforming struct 'Iterator' has non-sendable type 'Deque<Element>._Storage'; this is an error in the Swift 6 language mode
24 | public struct Iterator: IteratorProtocol {
25 | @usableFromInline
26 | internal var _storage: Deque._Storage
| `- warning: stored property '_storage' of 'Sendable'-conforming struct 'Iterator' has non-sendable type 'Deque<Element>._Storage'; this is an error in the Swift 6 language mode
27 |
28 | @usableFromInline
/home/andrew/ladybird-org/testing/swift-tests/swift-collections-no-undefined/build/_deps/swiftcollections-src/Sources/DequeModule/Deque._Storage.swift:15:10: note: consider making struct '_Storage' conform to the 'Sendable' protocol
13 | @frozen
14 | @usableFromInline
15 | struct _Storage {
| `- note: consider making struct '_Storage' conform to the 'Sendable' protocol
16 | @usableFromInline
17 | internal typealias _Buffer = ManagedBufferPointer<_DequeBufferHeader, Element>
[8/16] Linking Swift shared library lib/libDequeModule.so
FAILED: lib/libDequeModule.so
: && /home/andrew/.local/bin/swiftc -j 32 -num-threads 32 -emit-library -Xlinker -z -Xlinker defs -Xlinker --no-undefined -Xlinker --no-allow-shlib-undefined -Xlinker -soname -Xlinker libDequeModule.so -o lib/libDequeModule.so _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+Codable.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+Collection.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+CustomReflectable.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+Descriptions.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+Equatable.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+ExpressibleByArrayLiteral.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+Extras.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+Hashable.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+Sendable.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque+Testing.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._Storage.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._UnsafeHandle.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/_DequeBuffer.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/_DequeBufferHeader.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/_DequeSlot.swift.o _deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/_UnsafeWrappedBuffer.swift.o -L /home/andrew/ladybird-org/testing/swift-tests/swift-collections-no-undefined/build/lib -Xlinker -rpath -Xlinker /home/andrew/ladybird-org/testing/swift-tests/swift-collections-no-undefined/build/lib: lib/libInternalCollectionsUtilities.so && :
error: link command failed with exit code 1 (use -v to see invocation)
_deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._Storage.swift.o:Deque._Storage.swift.o:function $s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF:(.text.$s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF+0x208): error: undefined reference to 'round'
_deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._Storage.swift.o:Deque._Storage.swift.o:function $s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF:(.text.$s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF+0x21f): error: undefined reference to 'rint'
_deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._Storage.swift.o:Deque._Storage.swift.o:function $s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF:(.text.$s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF+0x233): error: undefined reference to 'trunc'
_deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._Storage.swift.o:Deque._Storage.swift.o:function $s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF:(.text.$s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF+0x261): error: undefined reference to 'ceil'
_deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._Storage.swift.o:Deque._Storage.swift.o:function $s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF:(.text.$s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF+0x275): error: undefined reference to 'floor'
_deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._Storage.swift.o:Deque._Storage.swift.o:function $s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF:(.text.$s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF+0x2e3): error: undefined reference to 'ceil'
_deps/swiftcollections-build/Sources/DequeModule/CMakeFiles/DequeModule.dir/Deque._Storage.swift.o:Deque._Storage.swift.o:function $s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF:(.text.$s11DequeModule0A0V8_StorageV13_growCapacity2to8linearlyS2i_SbtF+0x2f7): error: undefined reference to 'floor'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
[11/16] Building Swift Module 'HashTreeCollections' with 85 sources
ninja: build stopped: subcommand failed.
Failed link due to missing libm symbols. Adding a link_libraries(m) before FetchContent_MakeAvailable fixes the issue.
If a BUILD_SHARED_LIBS=ON config on linux is not supported, then this can be worked around by explicitly setting it to off for SwiftCollections like so:
set(BUILD_SHARED_LIBS_SAVE ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
FetchContent_MakeAvailable(SwiftCollections)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVE})
The CMake configuration in this package is for compiling this code while building the Swift toolchain itself, while SwiftPM is not available. Other use is also okay, of course -- just keep in mind that this is not a stable feature; things may break with each new tag.
Patches fixing things outside of toolchain builds are welcome, too! But the configuration must keep working for that use case, no matter what.
Does that mean that swift-collections will be available as a dependency shipped with the toolchain for "external" consumers to use in the future? Similar to how swift-testing is handled?
Or is it more that external consumers of the library are expected to use SwiftPM rather than CMake to build swift-collections for their own projects? Until and unless it becomes easy to call into SwiftPM via e.g. ExternalProject with a handy wrapper CMake script to obtain dependencies for mixed Swift/C++ targets to link to, I don't think that's going to work for my project 😅.