C言語とSwiftを一つのプロジェクトで混在させてみる(OS X, Linux)

# 概要
* C言語で書かれたソースとSwiftで書かれたソースをコンパイルして一つの実行ファイルを作成する方法を書いてみる。
  * 応用編として、実行ファイルではなく動的ライブラリ(Swiftモジュール)を作成することも可能だけど、ここでは書いてない。
* OS XでもLinuxでもできる方法を書く。即ち、Xcodeは使わない。 

# 要約
1. clangでC言語ソースからオブジェクトファイルを作成
2. swiftcでオブジェクトファイルと一緒にSwiftソースをコンパイル
3. 以上(これだけで解った貴方はこの記事を読む必要はありません)

# 環境
* OS X = OS X El Capitan + Xcode 7.3 (Swift 2.2)
* Linux = CentOS 7 + Swift 2.2.1

```console:OS&#x0020;X
$ 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
```

```console:Linux
$ 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つだけ。

```console
$ ls
c.c  c.h  swift.swift
```
```c:c.h
#include <stdio.h>
void c_function(const char * string);
```
```c:c.c
#include "c.h"
void c_function(const char * string) {
  (void)printf("%s", string);
}
```
```swift:swift.swift
c_function("Hello, I'm Swift.\n")
```

# コンパイル手順

## C言語ソースからオブジェクトファイルを作成

```console:OS&#x0020;X,&#x0020;Linux共通
$ clang -c c.c -oc.o
$ ls
c.c  c.h  c.o  swift.swift
```
できた。
別にclangでなくてgccでもいい。

## オブジェクトファイルと一緒にSwiftソースをコンパイル

```console:OS&#x0020;X
$ 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.

```

```console:Linux
$ 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]

[^1]: OS Xでwarningが出てるけど実質無害。OS XのSwift 2.2のターゲットをみると`x86_64-apple-macosx10.9`となっているので、c.oも10.9のSDKを使ってコンパイルすればいいのかもしれない…が、ここでは無視。

# NG集
## -import-objc-header？

### なにそれ美味しいの？

なぜか`swiftc --help`としてもこのオプションは表示されない[^2]。

[^2]: 自分は"[Xcodeなしで楽しむSwift (REPLからライブラリ生成まで) #Objective-CやCをブリッジする](http://qiita.com/demmy/items/c4a6a430787d3097a6df#objective-cやcをブリッジする)"で知りました。

### -import-objc-headerがないと？

```console
$ 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`としたくなるけど…。

```console
$ 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を使わないと？

```console:OS&#x0020;X
$ 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でもオブジェクトファイルを作成してから、まとめてみる。
つまり…

```console:OS&#x0020;X,&#x0020;Linux共通
$ clang -c c.c -oc.o
$ ls
c.c  c.h  c.o  swift.swift
```

これは一緒。
次はSwiftでもオブジェクトファイルを作成する：

```console:OS&#x0020;X
$ 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
```

```console:Linux
$ 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をまとめる：

```console:OS&#x0020;X,&#x0020;Linux共通
$ 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`でコンパイル(リンク)はできるけど、実行しようとすると

```console:OS&#x0020;X
$ ./main
dyld: Library not loaded: @rpath/libswiftCore.dylib
  Referenced from: (略)./main
  Reason: image not found
Trace/BPT trap: 5
```

とか言われる。
…素直にswiftcを使おう。