swift-collections icon indicating copy to clipboard operation
swift-collections copied to clipboard

CMake multi-module shared library build does not build with -Wl,-z,defs and -Wl,--no-undefined,--no-allow-shlib-undefined on Linux

Open ADKaster opened this issue 1 year ago • 3 comments

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 main branch 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.

ADKaster avatar Dec 19 '24 20:12 ADKaster

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})

ADKaster avatar Dec 19 '24 21:12 ADKaster

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.

lorentey avatar May 19 '25 19:05 lorentey

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 😅.

ADKaster avatar May 19 '25 19:05 ADKaster