How to create a C++/Swift executable with CMake?

I want to create a simple C++ executable that just calls a Swift function from the C++ main.
So just something like add_executable(Interop main.cpp func.swift).
In Xcode this is easy, but I can't figure out how to get this to work with CMake. I used Apple's CMake example and the CMake modules they provided, but I get the error that _main is a duplicate symbol, probably because a separate main is created for the Swift file.

My example CMake looks like this, where _swift_generate_cxx_header is the function from Apple's example.

add_executable(Interop main.cpp func.swift)
target_compile_options(Interop PUBLIC "$<$<COMPILE_LANGUAGE:Swift>:-cxx-interoperability-mode=default>")

_swift_generate_cxx_header(Interop Interop/Interop-swift.h)

The entire test project can be found here.

Swift will try to treat single swift sources as top-level-code. Try passing -parse-as-library to the swift compiler and you shouldn’t see that anymore.

Thanks for your help @etcwilde . I naively tried to add the flag to the custom command from the example (see commit here):

  add_custom_command(OUTPUT ${header_path}
    DEPENDS ${_SwiftSources}
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    COMMAND
      ${CMAKE_Swift_COMPILER} -frontend -typecheck
      ${ARG_SEARCH_PATHS}
      ${_SwiftSources}
      ${SDK_FLAGS}
      -module-name "${ARG_MODULE_NAME}"
      -cxx-interoperability-mode=default
      -emit-clang-header-path ${header_path}
      -parse-as-library
    COMMENT
      "Generating '${header_path}'"
    COMMAND_EXPAND_LISTS)

But I still get the same error:

❯ cmake --build build                                        
[4/4] Linking Swift executable src/Interop
FAILED: src/Interop 
: && /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc -j 12 -num-threads 12 -emit-executable -o src/Interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk src/CMakeFiles/Interop.dir/main.cpp.o src/CMakeFiles/Interop.dir/func.swift.o  -L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx   -L /usr/lib/swift   -L /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/lib/swift   -L /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk -lc++ && :
duplicate symbol '_main' in:
    /Users/winfried.auner/Developer/swift_cpp_interop_cmake/build/src/CMakeFiles/Interop.dir/func.swift.o
    /Users/winfried.auner/Developer/swift_cpp_interop_cmake/build/src/CMakeFiles/Interop.dir/main.cpp.o
ld: 1 duplicate symbols
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

The error message shows the swiftc invocation with -emit-executable, that sounds also potentially problematic, but I don't see where that is coming from.
What did I do wrong?

You want to add the flag to your target at compile-time, not a link time.
Add this to your src/CMakeLists.txt file:

target_compile_options(Interop PRIVATE $<$<COMPILE_LANGUAGE:Swift>:-parse-as-library>)
2 Likes

Perfect, thank you! That works.

@etcwilde In my example I used

set_target_properties(Interop PROPERTIES Swift_MODULE_NAME "SwiftInteroppenheimer")

to control the namespace of my Swift functions. In Xcode the namespace defaults to the target name, which can cause problems if there are multiple targets like an executable and a gtest target.
Is there an equivalent Xcode options to control the namespace?