Building C++ and Swift with CMake

I'm trying to setup CMake with Swift and running into issues when compiling cute.

Here's the repo: GitHub - pusewicz/dungeon_loop_cute

And the command I use: cmake -Bbuild -G Ninja . && cmake --build build.

Unfortunately I'm getting:

$ cmake --build build
[1/1] Linking Swift executable dungeonloop
FAILED: dungeonloop CMakeFiles/dungeonloop.dir/src/main.swift.o
: && /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc -j 16 -num-threads 16 -emit-executable -o dungeonloop -emit-dependencies -DCF_STATIC -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk -output-file-map CMakeFiles/dungeonloop.dir//output-file-map.json -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk -I /Users/piotr/Work/Github/dungeon_loop_cute/src -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-src/include -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-src/libraries -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-src/libraries/cimgui -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-src/libraries/cimgui/imgui -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/sdl2-build/include -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/sdl2-build/include-config- -F /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-build /Users/piotr/Work/Github/dungeon_loop_cute/src/main.swift  -F /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-build  -L /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-build  -L /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-build  -L /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/sdl2-build  -L /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/sdl2-build   -L /opt/homebrew/lib   -L /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk/usr/lib/swift _deps/cute-build/cute.framework/Versions/1.0.0/cute  -Xlinker -framework -Xlinker IOKit  -Xlinker -framework -Xlinker Foundation  -Xlinker -framework -Xlinker Security  -Xlinker -framework -Xlinker QuartzCore  -Xlinker -framework -Xlinker Metal  -Xlinker -framework -Xlinker MetalKit  -Xlinker -framework -Xlinker Network  _deps/cute-build/libphysfs.a  _deps/sdl2-build/libSDL2main.a  _deps/sdl2-build/libSDL2.a  -lm  -liconv  -Wl,-framework,CoreVideo  -Wl,-framework,Cocoa  -Wl,-framework,IOKit  -Wl,-framework,ForceFeedback  -Wl,-framework,Carbon  -Wl,-framework,CoreAudio  -Wl,-framework,AudioToolbox  -Wl,-framework,AVFoundation  -Wl,-framework,Foundation  -Wl,-weak_framework,GameController  -Wl,-weak_framework,Metal  -Wl,-weak_framework,QuartzCore  -Wl,-weak_framework,CoreHaptics  -lc++ && :
error: unknown argument: '-Wl,-framework,CoreVideo'
ninja: build stopped: subcommand failed.

How would I go about resolving this issue?

This is coming from SDL assuming that everyone is using a C/C++ compiler:

For Swift, the flag is -Xlinker, not -Wl, which is why Swift is complaining there. CoreVideo just happens to be the first framework getting linked, so it fails.

SDL is getting built as a static library, which means that linking its dependencies is the job of the dynamically linked DSO (dylib or executable) that links SDL. Your executable depends on cute, which by default builds as a static archive, so your executable is the first DSO in the chain, so that's what gets all of the link flags. If you build cute as a dynamic library/framework, it will still fail though because cute marks the library link PUBLIC, so everything that it depends on also gets forwarded to everything that links cute.

If you change that line to target_link_libraries(cute PRIVATE ${CF_LINK_LIBS}) and build cute as a dynamic library by setting CF_FRAMEWORK_STATIC to FALSE, your program should build.

@etcwilde Thanks so much for your reply. I'm very new to Swift, CMake so might be asking silly questions here.

target_link_libraries(cute PRIVATE ${CF_LINK_LIBS})

This, I imagine, means that I cannot override it in my CMakeList.txt and has to be changed in the upstream (cute)? Or is there a way to patch it without commiting Cute to my local repo?

You can apply patches in FetchContent_Declare with the PATCH_COMMAND.

It'll probably look something like

FetchContent_Declare(
  cute
  GIT_REPOSITORY https://github.com/RandyGaul/cute_framework
  PATCH_COMMAND git apply "<wherever you're putting the patch>/000-cute.patch"
)

I also recommend pinning the checkout to a commit with GIT_TAG so that you're not working against a moving target (Also helps with rebuild times since CMake doesn't need to check against the repo to see if anything has changed.) And it probably wouldn't hurt to open an issue/PR on cute to fix that as well.

1 Like

@etcwilde Fantastic, it looks like it managed to build.

I've pushed the changes and added build.sh script to run the build.

Now, the remaining task is to try to load that in my main.swift.

FAILED: dungeonloop CMakeFiles/dungeonloop.dir/src/main.swift.o
: && /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc -j 16 -num-threads 16 -emit-executable -o dungeonloop -emit-dependencies  -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk -output-file-map CMakeFiles/dungeonloop.dir//output-file-map.json -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk -I /Users/piotr/Work/Github/dungeon_loop_cute/src -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-src/include -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-src/libraries -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-src/libraries/cimgui -I /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-src/libraries/cimgui/imgui -F /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-build /Users/piotr/Work/Github/dungeon_loop_cute/src/main.swift  -F /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-build  -L /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-build -Xlinker -rpath -Xlinker /Users/piotr/Work/Github/dungeon_loop_cute/build/_deps/cute-build  _deps/cute-build/cute.framework/Versions/1.0.0/cute && :
/Users/piotr/Work/Github/dungeon_loop_cute/src/module.modulemap:2:19: error: umbrella header 'cute.h' not found
  umbrella header "cute.h"
                  ^
/Users/piotr/Work/Github/dungeon_loop_cute/src/main.swift:1:8: error: could not build Objective-C module 'cute'
import cute
       ^
error: fatalError
ninja: build stopped: subcommand failed.

I've added a modulemap file, but I don't really know how to expose the built framework. Would love some help with that!

I tried following Swift.org - Wrapping C/C++ Library in Swift, but to no avail. There should be a demo repo that does what this article tries to accomplish.