[cmake-developers] Adding Swift support to CMake for Linux/Makefiles

Eric Wing ewmailing at gmail.com
Wed Jan 13 07:00:39 EST 2016


On 1/12/16, Brad King <brad.king at kitware.com> wrote:
> On 01/08/2016 06:15 PM, Eric Wing wrote:
>> simple 'swiftc' integration going for ADD_EXECUTABLE, as described in
>> the original post.
>
> Take the generator check out of Modules/CMakeDetermineSwiftCompiler.cmake
> to get rid of the up-front error.  See "Modules/CMakeAddNewLanguage.txt"
> (many of the steps are already done).
>
> Edit Modules/CMakeSwiftInformation.cmake and add settings for variables
> like CMAKE_Swift_COMPILE_OBJECT and CMAKE_Swift_LINK_EXECUTABLE.  See
> the CMake{C,CXX,Fortran}Information.cmake modules for examples.  The
> available placeholders are in cmLocalGenerator::ExpandRuleVariable.
>
> If all the compile/link steps used for C and C++ map well to switfc
> then it should be fairly straightforward.  If other steps are needed
> or the steps have wildly different semantics then more in-depth changes
> to the actual generators may be needed.
>
> -Brad
>


So I got the trivial base case working with swiftc (no library
dependencies, Mac for now but will shift to Linux eventually):
add_exectuable(MyTarget main.swift)

Yay.

So next I wanted to add more files to the mix. And now I have some problems.


Problem 1: Intermixing C and Swift
add_exectuable(MyTarget main.swift some_c.c)
Note: I presume C++ (and Obj-C) could also be in the mix:
add_exectuable(MyTarget main.swift some_c.c some_cpp.cpp some_objc.m)

It looks like the link phase is invoking cc on my system (the C linker
rules?) instead of the Swift linker commands.

If my hunch is correct, how do I tell CMake to ‘promote’ Swift as the
correct tool?
(Perhaps there is a similar existing mechanism already is handling
intermixing C/C++ or C/Obj-C?)

Additional note: I am a little surprised to see cc instead of ld (or
swiftc). Now I may be able to use cc or ld, but I need to figure out
how to adapt and add some additional flags. So it could be that I
could work around this problem. (Kind of working solution at the
bottom.)



Problem 2: Multiple Swift files in the target
add_exectuable(MyTarget main.swift OtherSwift.swift)

With two or more files, I was hoping to do a single one-shot call:
swiftc -c OtherSwift.swift main.swift -module-name MyTarget

But it looks like CMake invokes each file separately. The problem that
creates in the above case is that if main.swift uses stuff from the
other file, it can no longer compile because it is unable to find the
code in OtherSwift because they are treated as separate unconnected
modules. I haven’t figured a way yet around this using swiftc.

However, I can switch to the tool ‘swift’, but it looks like Swift has
a similar constraint which I will discuss shortly.

But before I move on to that, is there a way to get CMake to invoke
all the Swift files in the target together? (It would need to exclude
any non-Swift files in the list.)

I think there may be trade-offs here. On one hand, file-by-file might
get us incremental compilation. But there is a flag called
-whole-module-optimization which suggests treating them as a batch
could allow better code generation.


Now for the 'swift' tool:

So the swift tool can go file-by-file, but the additional constraint
is that all the other Swift files in the target must be listed too. To
distinguish which file is actually being processed, you must use the
-primary-file <active_file.swift> <other_files>. I think this is so
Swift can figure out all the cross-file dependencies since there are
no header files in Swift.

Here is an example of compiling my two files:

# Compile main.swift to main.o
swift -frontend -c -primary-file
/Volumes/DataPartition/Users/ewing/Source/CodeTest/CMakeSwiftBasic/main.swift
/Volumes/DataPartition/Users/ewing/Source/CodeTest/CMakeSwiftBasic/OtherSwift.swift
-emit-module -module-name MyTarget  -o CMakeFiles/MyApp.dir/main.o
-emit-module-path CMakeFiles/MyApp.dir/main~partial.swiftmodule

# Compile OtherSwift.swift to OtherSwift.o
swift -frontend -c -primary-file
/Volumes/DataPartition/Users/ewing/Source/CodeTest/CMakeSwiftBasic/OtherSwift.swift
/Volumes/DataPartition/Users/ewing/Source/CodeTest/CMakeSwiftBasic/main.swift
-emit-module -module-name MyTarget  -o
CMakeFiles/MyApp.dir/OtherSwift.o -emit-module-path
CMakeFiles/MyApp.dir/OtherSwift~partial.swiftmodule

Other things to notice:
- I am using the CMake Target Name for the -module-name
- There is a basename~partial.swiftmodule being generated. I'm not
completely sure why this is needed, but I see it in the Xcode build
lines and other examples on the web.


Finally, the link line can look like this: (Xcode invokes clang
instead of cc, but they map to the same thing on Mac)

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
 -arch x86_64 -isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk
-L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static/macosx
-Xlinker -force_load_swift_libs -lswiftRuntime -lc++ -framework
Foundation    CMakeFiles/MyApp.dir/OtherSwift.o
CMakeFiles/MyApp.dir/main.o  -o MyApp



So in the CMake rules, for CMAKE_Swift_COMPILE_OBJECT, I think I need
something kind of like:

set(CMAKE_Swift_COMPILE_OBJECT

	  "<CMAKE_Swift_COMPILER> -frontend -c <INCLUDES> <FLAGS>
-primary-file <SOURCE> <SOURCES> -emit-module -module-name <TARGET> -o
<OBJECT>  -emit-module-path   -c <SOURCE>~partial.swiftmodule"
)

Additional notes:
- SOURCES doesn't actually exist in CMake, but I saw something called
OBJECTS which makes me think they are a similar concept.

- Also, for the second <SOURCES>, I need to remove the one I'm using
from my -primary-file. And there shouldn't be any non-Swift files in
the list (no C/C++/Obj-C).

- I actually need the basename of <SOURCE> for the ~partial.swiftmodule stuff.

- I'm not sure if I'm responsible for the intermediate products
directories. Do I need to explicitly concat the paths to any of these
things.

- I haven't added the bridging header flag/value yet. I need some
expected variable that holds it.




Can you give me some suggestions on what to look at next?

Thanks,
Eric


More information about the cmake-developers mailing list