clang でビルドしようとしたら何か言われた。
$ clang++ <中略> -lほげほげ
/usr/bin/ld: cannot find -lほげほげ
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
ここで仮に -lほげほげ
とした libほげほげは /usr/local/lib にあるもので、 -L/usr/local/lib
を足せば問題なくビルドできる。
ただどうも腑に落ちないのは、/usr/local/lib は当然 /etc/ld-elf.so.conf
で設定済みであること。
$ ldconfig -r |grep ほげほげ
329:-lほげほげ.5 => /usr/local/lib/libほげほげ.so.5
ld 側で調査してみても、
$ ld -l ほげほげ
ld: 警告: エントリシンボル _start が見つかりません。開始アドレスを設定しません
//lib/libc.so.7: `__progname' に対する定義されていない参照です
//usr/local/lib/libほげほげ.so.5: `environ' に対する定義されていない参照です
まあ何か言われるけどとりあえず通ってる。
じゃあ何なんだよと clang++ に -v
をつけて動かしてみると、
$ clang++ -v <中略> -lほげほげ
FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512
Target: x86_64-unknown-freebsd10.1
Thread model: posix
Selected GCC installation:
"/usr/bin/ld" <中略> -lほげほげ <後略>
/usr/bin/ld: cannot find -lほげほげ
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
何がおかしいのかわかんなすぎて頭抱えたんだけど、
$ which ld
/usr/local/bin/ld
ということで clang が中で動かしてた ld (/usr/bin/ld) とさっきコマンドラインで調査した ld (/usr/local/bin/ld) とでパスが違う。
$ /usr/bin/ld -v
GNU ld 2.17.50 [FreeBSD] 2007-07-03
$ /usr/local/bin/ld -v
GNU ld (GNU Binutils) 2.24
バージョンも違う。
ということで /usr/bin/ld でまた調べてみる。
$ /usr/bin/ld -l ほげほげ
/usr/bin/ld: cannot find -lほげほげ
ビンゴ。たしかに自分の環境では $PATH
内で /usr/local/bin は /usr/bin より前にある。
さてここで /usr/bin/ld が ldconfig の設定を見てくれてないのが気になるところだけど、こいつを消して /usr/local/bin/ld にシンボリックリンクを貼るなどの下手なハックで色々やっちゃうと何かヤバい気がする(/usr 以下で /usr/local 以下でないものに対する不可侵感みたいなのがあるマン)ので、clang++ が使う ld を設定とかでどうにかならないか頑張ってみる。
まずは一旦 /usr/bin/ld を見えなくしてどうなるか実験。
$ sudo mv /usr/bin/ld{,.bak}
$ clang++ -v <中略> -lほげほげ
FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512
Target: x86_64-unknown-freebsd10.1
Thread model: posix
Selected GCC installation:
"/usr/local/bin/ld" <中略> -lほげほげ <後略>
$ sudo mv /usr/bin/ld{.bak,}
通っちゃった。とりあえず clang の中で ld ベタ書きってわけではないっぽい。
ということでコード覗き。ちなみに releng/10.1 の rev.274550 です。clangのバージョンはこんなかんじ。
$ clang -v
FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512
Target: x86_64-unknown-freebsd10.1
Thread model: posix
Selected GCC installation:
ld を呼んでるところは /usr/src/contrib/llvm/tools/clang/lib/Driver/Tools.cpp の、freebsd::Link::ConstructJob()
。
ここの一番下で、
const char *Exec =
Args.MakeArgString(ToolChain.GetProgramPath("ld"));
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
}
とかやってる。この GetProgramPath()
の実体は /usr/src/contrib/llvm/tools/clang/lib/Driver/Driver.cpp の std::string Driver::GetProgramPath()
で、そのなかの1つめの for ループで prefix におけるコマンドの検索、それが見つからないと 2つめの for ループで $PATH におけるコマンドの検索、という動きをしてる。
$ which clang++
/usr/bin/clang++
なので、たしかに prefix は /usr で、つまりそのせいで $PATH において優先度の高かった /usr/local/bin/ld ではなく /usr/bin/ld が呼ばれてるというところまでの説明がついた。
ここまでくれば話は早くて、ports で language/clangXX のどれかを入れてコンパイルはそっちを使うとか、もっと雑に
$ ln -s /usr/bin/clang /usr/local/bin/clang
$ ln -s /usr/bin/clang++ /usr/local/bin/clang++
$ ln -s /usr/bin/clang-cpp /usr/local/bin/clang-cpp
$ ln -s /usr/bin/clang-tblgen /usr/local/bin/clang-tblgen
とかやってしまえば良い。実際後者で試してみて問題なくコンパイルできた。ということでFIX.