8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Homebrewで入れたC++ライブラリはclangでコンパイルしないとダメらしい

Posted at

なにこれ

C++をついこの間使い始めました. 外部ライブラリを使おうと思って, Homebrewからぶち込んでGCCでコンパイルして使おうと思ったら使えなかった. そういうお話.

環境

こういう困ったことの解決策を共有するときは計算環境を提示しておいたほうがいいよね. 足りなかったら言ってくだいね.

OS           : macOS Mojave ver10.14
c++ compiler : gcc version 8.2.0 (Homebrew GCC 8.2.0)
homebrew     : Homebrew 1.8.6

Homebrewってなんぞや

そんなん知ってるわっていう人が大半かもしれないけど一応. 〇〇をインストールしますって言って,

brew install 〇〇

しか書いてないみたいなのがとても嫌なので.
Homebrewとは, macOS用のパッケージ管理システムの1つで, ソフトウェアを簡単に入れるためのものだそうです. ふむふむ. 手動で入れようと思うとmakeが通らねえーとかパスが違うーとかままありますよね. そういっためんどくさいもんを勝手に解決してコマンド1つでソフトウェアがぶっ込める. そんな素敵なものがHomebrewなんだそうだ.

Homebrew自体のインストールはこちらからどうぞー Homebrew

とりあえず最初にコケるまで

外部ライブラリとしてICU - International Components for Unicodeを使おうと思って, とりあえずHomebrewで検索.

brew_search
brew search icu

そうすっとicu4cなるものがヒット. 今考えるとこれicu for Cの意味だったのねとわかるんだけど, 始めはなにこの4cってずっと思ってた. まあそんなことはいいとして, わーい見つかったということでインストール.

brew_install
brew install icu4c

はい, 完全勝利. あとは使うだけ!そう思っていた頃が僕にもありました. てなわけでコンパイル

compile
g++ icu_test.cpp
compile_error
icu_test.cpp:1:10: fatal error: 'unicode/translit.h' file not found
#include <unicode/translit.h>
         ^~~~~~~~~~~~~~~~~~~~
1 error generated.

あっるえー??ヘッダファイルが見つかっていないらしい. 一生懸命調べるとCコンパイラでは-Iオプションでヘッダファイルのインクルードパス, -Lでその実装が書かれたライブラリパスを指定するとのこと.
いやそのパスってどこにあんねんということで, findコマンドで探した.
optの方でいいんじゃねって思って, 中身を見る.

findコマンド
xxxxxx$ find /usr/local/ -name icu4c
/usr/local//opt/icu4c
/usr/local//Cellar/icu4c
xxxxxx$ ls /usr/local//opt/icu4c
INSTALL_RECEIPT.json	include			readme.html
LICENSE			lib			sbin
bin			license.html		share

なんかいかにもなものがある. includelibってのがある. これじゃね?って思ってもう一回コンパイル.

compile
g++ icu_test.cpp -I/usr/local/opt/icu4c/include -L/usr/local/opt/icu4c/lib
compile_error
Undefined symbols for architecture x86_64:
  "icu_63::UnicodeString::UnicodeString(char const*)", referenced from:
      katakana2hiragana(icu_63::UnicodeString)       in ccz5k34Q.o
  "icu_63::UnicodeString::~UnicodeString()", referenced from:
      katakana2hiragana(icu_63::UnicodeString)       in ccz5k34Q.o
  "icu_63::Transliterator::createInstance(icu_63::UnicodeString const&, UTransDirection, UErrorCode&)", referenced from:
      katakana2hiragana(icu_63::UnicodeString)       in ccz5k34Q.o
  "icu_63::UnicodeString::extract(int, int, char*, unsigned int, char const*) const", referenced from:
      icu_63::UnicodeString::extract(int, int, char*, char const*) const in ccz5k34Q.o
  "operator new[](unsigned long)", referenced from:
      katakana2hiragana(icu_63::UnicodeString)       in ccz5k34Q.o
  "___gxx_personality_v0", referenced from:
      Dwarf Exception Unwind Info (__eh_frame) in ccz5k34Q.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status

は???ってなった. いやいやインクルードパスとライブラリパスは書いてあるやんけ!!
さらに頑張って調べてみると, これはリンカってもののエラーらしいことがわかった. ライブラリパスを-Lオプションで記載するだけではなく, その中のどのライブラリをくっつけて実行可能バイナリを生成するかってことを-lオプションで指定する必要があるらしい.

libraries
xxxxxx$ ls /usr/local/opt/icu4c/lib
icu			libicuio.63.1.dylib	libicutu.63.dylib
libicudata.63.1.dylib	libicuio.63.dylib	libicutu.a
libicudata.63.dylib	libicuio.a		libicutu.dylib
libicudata.a		libicuio.dylib		libicuuc.63.1.dylib
libicudata.dylib	libicutest.63.1.dylib	libicuuc.63.dylib
libicui18n.63.1.dylib	libicutest.63.dylib	libicuuc.a
libicui18n.63.dylib	libicutest.a		libicuuc.dylib
libicui18n.a		libicutest.dylib	pkgconfig
libicui18n.dylib	libicutu.63.1.dylib

なんかたくさんある. lib〇〇っていうのがそれぞれライブラリらしい. どれを使えばいいかわからないので, とりあえず全部のせしてみよう. コンパイルが通ったら不要な物を減らして再コンパイルすればいいしね.

compile
g++ icu_test.cpp -I/usr/local/opt/icu4c/include -L/usr/local/opt/icu4c/lib -licudata -licui18n -licuio -licutest -licutu -licuuc
compile_error
Undefined symbols for architecture x86_64:
  "operator new[](unsigned long)", referenced from:
      katakana2hiragana(icu_63::UnicodeString)       in cc1qyWVv.o
  "___gxx_personality_v0", referenced from:
      Dwarf Exception Unwind Info (__eh_frame) in cc1qyWVv.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status

これもうわかんねえな. 一旦お手上げもうだめ.

ライブラリインストール後のコンパイルをGCCにやらせてみる

さらに頑張って調べてみると, どうやらHomebrewで入るライブラリは, デフォルトではmacに最初からインストールされているclangでコンパイルされるらしい. てことは, clangでコンパイルしたライブラリをgccでコンパイルしたコードとgccでリンクしようとして上手く行ってないのではないかと推測.

stackoverflow - Using Homebrew with alternate GCC

このあたりを参考にビルド時のコンパイラをgccにしてHomebrewから再インストール

brew_install_with_GCC
brew uninstall icu4c
HOMEBREW_CXX=g++ brew install icu4c

これでもう1回コンパイル. だけどダメ. もはやエラーメッセージをとっておくことも忘れた(´•ω•`)

ソースコードのコンパイルをclangにやらせてみる

ライブラリをgccでコンパイルしてダメなら, 今度はその逆, 自分のコードをclangでコンパイルすればいいのではないか. やってみる.

brew_install_with_clang
brew uninstall icu4c
HOMEBREW_CXX=c++ brew install icu4c

これでもう一回コンパイル.

compile
c++ icu_test.cpp -I/usr/local/opt/icu4c/include -L/usr/local/opt/icu4c/lib -licudata -licui18n -licuio -licutest -licutu -licuuc

ようやく通った!!やった!もう外部ライブラリなんて怖くない(大嘘)
Homebrewのデフォルトのコンパイラがclangだから, Homebrewからインストールできるソフトウェアはそれで動くようにできているんだろうなぁ.

まとめ

C++で外部ライブラリを使うときには

  • -Iオプションでヘッダファイルがあるディレクトリのパスを指定する必要がある
  • -Lオプションでライブラリの実装があるファイルへのお明日を指定する必要がある
  • -lオプションで使用したいライブラリ名を指定する必要がある
  • Homebrewからインストールしたライブラリを使用する場合にはgccではなくclangでコンパイルする必要がある

何はともあれ, 解決してよかった.

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?