macOS `Catalina`に`clang 12.0.0`を入れて`ruby 3.0.0`を入れるだけ

ある日,rbenvを見ていた

brew upgrade ebenvして,

$ rbenv install -list
2.5.8
2.6.6
2.7.2
3.0.0
jruby-9.2.14.0
mruby-2.1.2
rbx-5.0
truffleruby-20.3.0
truffleruby+graalvm-20.3.0

昨年中にruby 3.0.0が正式にリリースされ,rbenvも対応したらしい.入れてみよっかなぁ〜俺もなぁ〜…

そのruby 3.0.0を入れようとしてしくじった

当時の環境は以下の通りだった.

  • MBP 2016 15inch
  • macOS Catalina
    • clang Apple 11.0.0←だいたいこいつのせいだった

吐かれたエラー

...
linking miniruby
generating x86_64-darwin19-fake.rb
x86_64-darwin19-fake.rb updated
make: *** [exe/ruby] Segmentation fault: 11
make: *** Deleting file 'exe/ruby'

BUILD FAILED (Mac OS X 10.15.7 using ruby-build 20201225)

Inspect or clean up the working tree at /var/folders/rk/0hczm32x2znb6pr6h4858hpc0000gn/T/ruby-build.20210104003100.75691.mUe6Zo
Results logged to /var/folders/rk/0hczm32x2znb6pr6h4858hpc0000gn/T/ruby-build.20210104003100.75691.log

Last 10 log lines:
compiling enc/unicode.c
compiling enc/utf_8.c
compiling enc/trans/newline.c
./revision.h unchanged
compiling version.c
linking miniruby
generating x86_64-darwin19-fake.rb
x86_64-darwin19-fake.rb updated
make: *** [exe/ruby] Segmentation fault: 11
make: *** Deleting file 'exe/ruby'

どうやら最後のmakeのとこでしくじったらしい.

原因推定

Segmentation fault: 11,これはなんかコンパイラが悪い気がしたので,設定や経過を見たくなった.

rbenv install -v 3.0.0

-vしながらrbenv installすると諸々の経過が見える,その結果

Configuration summary for ruby version 3.0.0

   * Installation prefix: /Users/username/.rbenv/versions/3.0.0
   * exec prefix:         ${prefix}
   * arch:                x86_64-darwin19
   * site arch:           ${arch}
   * RUBY_BASE_NAME:      ruby
   * enable shared:       yes
   * ruby lib prefix:     ${libdir}/${RUBY_BASE_NAME}
   * site libraries path: ${rubylibprefix}/${sitearch}
   * vendor path:         ${rubylibprefix}/vendor_ruby
   * target OS:           darwin19
   * compiler:            clang -fdeclspec
   * with pthread:        yes
   * with coroutine:      amd64
   * enable shared libs:  yes
   * dynamic library ext: bundle
   * CFLAGS:              ${optflags} ${debugflags} ${warnflags}
   * LDFLAGS:             -L. \
                          -L/Users/username/.rbenv/versions/3.0.0/lib  \
                          -fstack-protector-strong -L/usr/local/lib
   * DLDFLAGS:            -L/Users/username/.rbenv/versions/3.0.0/lib  \
                          -Wl,-undefined,dynamic_lookup \
                          -Wl,-multiply_defined,suppress
   * optflags:            -O3
   * debugflags:          -ggdb3
   * warnflags:           -Wall -Wextra -Wdeprecated-declarations \
                          -Wdivision-by-zero \
                          -Wimplicit-function-declaration -Wimplicit-int \
                          -Wpointer-arith -Wshorten-64-to-32 \
                          -Wwrite-strings -Wmissing-noreturn \
                          -Wno-constant-logical-operand -Wno-long-long \
                          -Wno-missing-field-initializers \
                          -Wno-overlength-strings -Wno-parentheses-equality \
                          -Wno-self-assign -Wno-tautological-compare \
                          -Wno-unused-parameter -Wno-unused-value \
                          -Wunused-variable -Wextra-tokens
   * strip command:       strip -A -n
   * install doc:         rdoc
   * JIT support:         yes
   * man page type:       doc
   * BASERUBY -v:         ruby 2.7.2p137 (2020-10-01 revision 5445e04352) \
                          [x86_64-darwin19]

を吐きながら,同じように失敗した.

   * target OS:           darwin19
   * compiler:            clang -fdeclspec

を見るに,macOS備え付けのclangを使っていることは察せた(私はいまだにコンパイラに手を出した事がない).

$ clang -v
Apple clang version 11.0.0 (clang-1100.0.33.8)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

どうもビルドにはコイツを使ったらしい. 悩んでTwitterを彷徨っていたら,clang Apple 12.0.0ruby 3.0.0を入れたっぽい方を発見した. またこ↑こ↓によると,macOS Big SurでちゃんとXCodeを入れている人は

$ clang --version
Apple clang version 12.0.0 (clang-1200.0.32.27)
Target: x86_64-apple-darwin20.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

となるらしい(私はXCodeを入れた後に消した,だってあれデカイし重いし…).

つまりclangのバージョンが古いせいでしくじったんじゃね?と考えた.

解決編

概要

CommandLineToolsを強制的に入れ直すとclang 12.0.0になるのでヨシ!ruby 3.0.0も入れられる!

手順

Catalinasoftowareupdateに任せてもそれ以上にCommandLineToolsは新しくならない(つまりclang 12.0.0はどう足掻いても手に入らない)ので,いっそ手動で入れ直す.

$ sudo rm -rf /Library/Developer/CommandLineTools
...
$ xcode-select --install

バージョン確認すると

$ clang -v
Apple clang version 12.0.0 (clang-1200.0.32.28)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

こうなるはず,やっぱりXCodeがなくても大丈夫だったじゃないか…

これで

$ rbenv install 3.0.0

すれば

Installed ruby-3.0.0 to /Users/username/.rbenv/versions/3.0.0

が出るはず,バージョンを変えるか変えないかはあなた次第だが

rbenv local 3.0.0

とかでできる.

終わり!閉廷!以上!みんな解散!君もう帰って良いよ

おまけ

clang 11.0.0(やそれ以前の任意のバージョンのclang)を使いたい時にどうするかを考えた.

概要

homebrewllvmを入れて,それ経由でclangのバージョンを任意に変えられるようにしようぜ?

llvmとは

llvmを開発している組織さんの公式ページはこ↑こ↓llvmまわりのダウンロードやドキュメント等はこ↑こ↓llvm自体はcc++コンパイラclangのバックで動いているような中間言語を生成するシステムで,というかclangcの仕様に対応しながらllvmと並行で開発・維持され続けているものになっている…らしいな? つまりある時からllvmのバージョンがclangのバージョンになっている,そしてbrewで手に入れられる最新版は今だとllvm 11.0.0で,それを入れるとclang 11.0.0がもれなくそれについてくる…という流れになる.

ただあくまで言語やアーキテクチャに依存せずより効率の良い中間言語を生成しようとするのがllvmで,それを利用してobjective-cなどをコンパイルするのがclang,そんでclangの開発の言い出しっぺはやっぱりAppleで…だからあっXCodeにちゃんと最新版のclang 12.0.0が入って提供されている,やっぱ林檎ってすげぇわマジで…

…なのでclanggccよりも性能と使い勝手が共に良い…らしい(私は開発者ではないので全くわからないが).

事前準備

homebrewを導入しておく.

最新版のllvmだけで良いなら

まずは入れてみる.

$ brew install llvm <versions>
...
$ brew info llvm
...

ちゃんと注意書きを読む(抜粋).

To use the bundled libc++ please add the following LDFLAGS:
  LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib"

llvm is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.

If you need to have llvm first in your PATH run:
  echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> /Users/SolunaEureka/.bash_profile

For compilers to find llvm you may need to set:
  export LDFLAGS="-L/usr/local/opt/llvm/lib"
  export CPPFLAGS="-I/usr/local/opt/llvm/include"

keg-only…つまりシンボリックリンクが生成されないタイプのパッケージだけど,中身はちゃんとあるからパスだけは通そうね!あとコンパイラに知らせたいフラッグにも対応しているから,使いたければパスだけは通そうね!(意訳)

…ということで,コマンドとしてのllvmはどうもなさそうなので,それを打ってllvm経由のclangが有効になるような感じにするために,.bash_profileをいじる(参考:学生たちの技術ブログ).まだbashでごめんなさい

function llvm (){ #関数を定義している
    export PATH="/usr/local/opt/llvm/bin:$PATH" #以下,先の注意書きの通りにパスを追加・変更する
    export LDFLAGS="-L/usr/local/opt/llvm/lib"
    export CPPFLAGS="-I/usr/local/opt/llvm/include"
    unset -f llvm #いったん呼び出されたら定義されたこの関数を破壊する
}

ここまで終わったらterminalを立ち上げ直して

$ llvm
$ clang -v
clang version 11.0.0
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin

になるはず. 新しいタブを開けば元に戻るので安心だね!

旧版のllvmを使いたいなら

homebrewは基本的に最新版以外は絶対に認めないマンな上に過去版のインストールに関しては仕様が変わるらしい,そんな中で個人的に現在の参考になったのはこ↑こ↓brew tap-newbrew extractを用いて,自家製のtapに公式のformulaをぶち込んで,そのtapから更にbrew installすれば良い,らしい.これを発案した人,本当に賢い…

ということで試しにllvm 10.0.0を入れてclang 10.0.0に切り替えようとしてみる(主要な出力を抜き出す).

$ brew tap-new llvm/1000
Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/llvm/homebrew-1000/.git/
[master (root-commit) d649d61] Create llvm/1000 tap
 3 files changed, 85 insertions(+)
 create mode 100644 .github/workflows/publish.yml
 create mode 100644 .github/workflows/tests.yml
 create mode 100644 README.md
==> Created llvm/1000
/usr/local/Homebrew/Library/Taps/llvm/homebrew-1000

$ brew extract llvm llvm/1000 --version 10.0.0
==> Searching repository history
==> Writing formula for llvm from revision 96544f6 to:
/usr/local/Homebrew/Library/Taps/llvm/homebrew-1000/Formula/llvm@10.0.0.rb

$ brew install llvm/1000/llvm@10.0.0
==> Installing llvm@10.0.0 from llvm/1000
llvm@10.0.0: A full installation of Xcode.app is required to compile
this software. Installing just the Command Line Tools is not sufficient.

Xcode can be installed from the App Store.
Error: An unsatisfied requirement failed this build.

XCodeがないとダメと言われた.なんだコレは,たまげたなぁ… もしちゃんと入れている人がいたら試してみて欲しい.

あとはどうにかしてバージョンを切り替えてみて欲しい…

感想

コンパイラって色々あって大変だな〜とづまりすとこ 絶対もっと良いやり方があるのでは?brewはバージョン管理に不向きだしllvmclangは公式サイトからDLして突っ込んで自分でちゃんと関数を組み上げた方がいいんじゃないか?問題は解決したからもう何も考えられないけど…