概要
- C言語で書かれたソースとSwiftで書かれたソースをコンパイルして一つの実行ファイルを作成する方法を書いてみる。
- 応用編として、実行ファイルではなく動的ライブラリ(Swiftモジュール)を作成することも可能だけど、ここでは書いてない。
- OS XでもLinuxでもできる方法を書く。即ち、Xcodeは使わない。
要約
- clangでC言語ソースからオブジェクトファイルを作成
- swiftcでオブジェクトファイルと一緒にSwiftソースをコンパイル
- 以上(これだけで解った貴方はこの記事を読む必要はありません)
環境
- OS X = OS X El Capitan + Xcode 7.3 (Swift 2.2)
- Linux = CentOS 7 + Swift 2.2.1
$ clang --version
Apple LLVM version 7.3.0 (clang-703.0.29)
Target: x86_64-apple-darwin15.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
$ swift --version
Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
Target: x86_64-apple-macosx10.9
$ clang --version
clang version 3.8.0 (tags/RELEASE_380/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
$ swift --version
Swift version 2.2.1 (swift-2.2.1-RELEASE)
Target: x86_64-unknown-linux-gnu
ソースファイル
説明を簡単にするために、今回用意したファイルは3つだけ。
$ ls
c.c c.h swift.swift
#include <stdio.h>
void c_function(const char * string);
#include "c.h"
void c_function(const char * string) {
(void)printf("%s", string);
}
c_function("Hello, I'm Swift.\n")
コンパイル手順
C言語ソースからオブジェクトファイルを作成
$ clang -c c.c -oc.o
$ ls
c.c c.h c.o swift.swift
できた。
別にclangでなくてgccでもいい。
オブジェクトファイルと一緒にSwiftソースをコンパイル
$ xcrun --sdk macosx swiftc swift.swift c.o -import-objc-header c.h
ld: warning: object file (c.o) was built for newer OSX version (10.11) than being linked (10.9)
$ ls
c.c c.h c.o main swift.swift
$ ./main
Hello, I'm Swift.
$ swiftc swift.swift c.o -import-objc-header c.h
$ ls
c.c c.h c.o main swift.swift
$ ./main
Hello, I'm Swift.
できた。これでおわり。
ね、簡単でしょう?1
NG集
-import-objc-header?
なにそれ美味しいの?
なぜかswiftc --help
としてもこのオプションは表示されない2。
-import-objc-headerがないと?
$ swiftc swift.swift c.o
swift.swift:1:1: error: use of unresolved identifier 'c_function'
c_function("Hello, I'm Swift.\n")
^~~~~~~~~~
…ですよね。
-import-c-headerではない。
C言語のヘッダなので-import-c-header
としたくなるけど…。
$ swiftc swift.swift c.o -import-c-header c.h
<unknown>:0: error: unknown argument: '-import-c-header'
…怒られた。
Objective-Cは言語仕様上C言語を完全に包含するので-import-objc-header
でCのヘッダを読み込んでも問題ないというのは、理論的に言えばそうなんだけど…なんか気持ち悪いような。
OS Xでxcrunを使わないと?
$ swiftc swift.swift c.o -import-objc-header c.h
(略)./c.h:1:10: error: 'stdio.h' file not found
#include <stdio.h>
^
<unknown>:0: error: failed to import bridging header 'c.h'
C言語の標準ライブラリのヘッダがみつからない。Linuxでは問題ないんだけど。
もう少し回りくどい方法
C言語ソースからオブジェクトファイルを作成したあとに、Swiftでもオブジェクトファイルを作成してから、まとめてみる。
つまり…
$ clang -c c.c -oc.o
$ ls
c.c c.h c.o swift.swift
これは一緒。
次はSwiftでもオブジェクトファイルを作成する:
$ xcrun --sdk macosx swiftc -c swift.swift -oswift.o -import-objc-header c.h
$ ls
c.c c.h c.o swift.o swift.swift
$ swiftc -c swift.swift -oswift.o -import-objc-header c.h
$ ls
c.c c.h c.o swift.o swift.swift
こうやってできたc.oとswift.oをまとめる:
$ swiftc swift.o c.o 2> /dev/null
$ ls
c.c c.h c.o main swift.o swift.swift
$ ./main
Hello, I'm Swift.
できた。
ちなみに、clang swift.o c.o -omain
とすると、libswiftCore.{dylib,so}はどこにあんねん、と言われる(Linuxではすごく分かりにくいエラーメッセージが出た)。
ということで、OS Xならclang swift.o c.o -omain -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -lswiftCore
でコンパイル(リンク)はできるけど、実行しようとすると
$ ./main
dyld: Library not loaded: @rpath/libswiftCore.dylib
Referenced from: (略)./main
Reason: image not found
Trace/BPT trap: 5
とか言われる。
…素直にswiftcを使おう。
-
OS Xでwarningが出てるけど実質無害。OS XのSwift 2.2のターゲットをみると
x86_64-apple-macosx10.9
となっているので、c.oも10.9のSDKを使ってコンパイルすればいいのかもしれない…が、ここでは無視。 ↩ -
自分は"Xcodeなしで楽しむSwift (REPLからライブラリ生成まで) #Objective-CやCをブリッジする"で知りました。 ↩