macOS Sonoma 14.1.2にrubyをinstallする

会社用のM3 Macが届いたので、さっそくrubyをinstallしようとしたら、エラーでハマったのでメモしておく。

エラー内容

$ RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl)" asdf install ruby 3.2.2
To follow progress, use 'tail -f /var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231203194651.23142.log' or pass --verbose
Downloading ruby-3.2.2.tar.gz...
-> https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.2.tar.gz
Installing ruby-3.2.2...
ruby-build: using readline from homebrew
ruby-build: using libyaml from homebrew
ruby-build: using gmp from homebrew

BUILD FAILED (macOS 14.1.2 using ruby-build 20230424)

Inspect or clean up the working tree at /var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231203194651.23142.RhNS49
Results logged to /var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231203194651.23142.log

Last 10 log lines:
checking for llvm-nm... llvm-nm
checking for llvm-objcopy... no
checking for llvm-objdump... llvm-objdump
checking for llvm-ranlib... no
checking for llvm-strip... no
checking for gcc... clang
checking whether the C compiler works... no
configure: error: in `/var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231203194651.23142.RhNS49/ruby-3.2.2':
configure: error: C compiler cannot create executables
See `config.log' for more details

ぱっと見

よく分からない。config.log も見てみるがこちらもイマイチ分からない。 ただ、llvm系の何かが足りておらず、Cコンパイラがうまく起動してないことだけは分かる。

MacのCコンパイラは Clang と呼ばれるもので、こいつはXCodeのCommandLineToolsに同梱されている。 とりあえずversionを見てみると、、、

$ clang --version
Apple clang version 15.0.0 (clang-1500.0.40.1)
Target: arm64-apple-darwin23.1.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

binの場所も表示されている。llvm系を確認すると、確かにinstall時のログに出ていたものが存在しないようだ。

$ ll /Library/Developer/CommandLineTools/usr/bin | grep llvm
lrwxr-xr-x  1 root  wheel    12B Dec  3 05:18 c++filt@ -> llvm-cxxfilt
lrwxr-xr-x  1 root  wheel    14B Dec  3 05:18 dwarfdump@ -> llvm-dwarfdump
lrwxr-xr-x  1 root  wheel     8B Dec  3 05:18 gcov@ -> llvm-cov
-rwxr-xr-x  1 root  wheel    12M Sep  7 07:24 llvm-cov*
-rwxr-xr-x  1 root  wheel   1.1M Sep  7 07:23 llvm-cxxfilt*
-rwxr-xr-x  1 root  wheel    23M Sep  7 07:24 llvm-dwarfdump*
-rwxr-xr-x  1 root  wheel    28M Sep  7 07:23 llvm-nm*
-rwxr-xr-x  1 root  wheel    31M Sep  7 07:24 llvm-objdump*
-rwxr-xr-x  1 root  wheel   166K Sep  7 07:24 llvm-otool*
-rwxr-xr-x  1 root  wheel    13M Sep  7 07:23 llvm-profdata*
-rwxr-xr-x  1 root  wheel    10M Sep  7 07:23 llvm-size*
lrwxr-xr-x  1 root  wheel     7B Dec  3 05:18 nm@ -> llvm-nm
lrwxr-xr-x  1 root  wheel    12B Dec  3 05:18 objdump@ -> llvm-objdump
lrwxr-xr-x  1 root  wheel    10B Dec  3 05:18 otool@ -> llvm-otool

とりあえずbinaryでversionを確認してみると、LLVMは version 15系ということがわかった。

$ llvm-cov --version
Apple LLVM version 15.0.0
  Optimized build.
  Default target: arm64-apple-darwin23.1.0
  Host CPU: apple-m1

homebrewでllvmを入れる

brewにあるだろうと思って、調べてみると、現在は17系のようだ。

$ brew info llvm
==> llvm: stable 17.0.6 (bottled), HEAD [keg-only]

とりあえず入れてみる。

$ brew install llvm

以下にbinがあったので確認してみる。たくさんあるので省略しますが、install時に「no」と表示されていたものがある。

$ ll /opt/homebrew/opt/llvm/bin | grep llvm
-r-xr-xr-x  1 rochefort  admin    37K Nov 28 17:52 llvm-PerfectShuffle*
lrwxr-xr-x  1 rochefort  admin    15B Nov 28 17:52 llvm-addr2line@ -> llvm-symbolizer
-rwxr-xr-x  1 rochefort  admin   108K Nov 28 17:52 llvm-ar*
....(略)

気を取り直してinstall

llvm install時とbrew info 時に以下のメッセージが表示されているので、それぞれ設定する。 (なぜかbinの方のメッセージはinstall時には出ていなかった)

If you need to have llvm first in your PATH, run:
  echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc

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

あとは、いつものようにinstallするだけ。

$ RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl)" asdf install ruby 3.2.2

最初llvm/clang周りは CommandLineTools を最新にしたり、beta版を使ったり、ダウングレードしたりすれば直る可能性があるので いろいろ試していたが、どれもダメだった。


追記

Operation not permitted @ dir_s_mkdir

現象

古いrubyだとこのエラーが出る。

/private/var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231204201622.35404.ClJqe6/ruby-3.1.3/lib/fileutils.rb:240:in `mkdir': Operation not permitted @ dir_s_mkdir - /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk/Users (Errno::EPERM)

対応

macPrivacy & Security 設定にterminalからchownできるようにしておきます。

あとは該当ディレクトリをchown

$ sudo chown $(whoami) /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk

参考

How Rails.env works with EnvironmentInquirer & StringInquirer (Example) | GoRails

How Rails.env works with EnvironmentInquirer & StringInquirer (Example) | GoRails
をみていました。
自然すぎて、気にもしなかった、Rails.env.development? のコード解説です。中身はエレガントな実装になってました。
12分ほどの動画なのでさらっと見れます。

rails consoleで確認

>> Rails.env
=> "development"
>> Rails.env.test?
=> false
>> Rails.env.development?
=> true
>> Rails.env == "test"
=> false

となりますが、これは一体どうやってるのか。

ActiveSupport::Stringinquirer

まずは、ActiveSupport::Stringinquirer。
こいつは、String を継承していて、? 付きのメソッド呼び出しがあれば、selfと?を削除した文字列との比較を行うようになってます。

# https://github.com/rails/rails/blob/v7.1.1/activesupport/lib/active_support/string_inquirer.rb
  class StringInquirer < String
    private
      def respond_to_missing?(method_name, include_private = false)
        method_name.end_with?("?") || super
      end

      def method_missing(method_name, *arguments)
        if method_name.end_with?("?")
          self == method_name[0..-2]
        else
          super
        end
      end
  end

要はこういうこと。

>> fruit = ActiveSupport::StringInquirer.new("apple")
=> "apple"
>> fruit.apple?
=> true
>> fruit.apple
/Users/rochefort/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/activesupport-7.0.4.2/lib/active_support/string_inquirer.rb:29:in `method_missing': undefined method `apple' for "apple":ActiveSupport::StringInquirer (NoMethodError)

>>fruit.orange?
=> false

Rails.env

続いて、Rails.env を見てみると、環境名を渡して、ActiveSupport::EnvironmentInquirer を newしています。

# https://github.com/rails/rails/blob/v7.1.1/railties/lib/rails.rb
module Rails
  class << self
    def env
      @_env ||= ActiveSupport::EnvironmentInquirer.new(ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development")
    end

ActiveSupport::EnvironmentInquirer

ここでは、initializeで環境ごとのインスタンス変数(@development, @test, @production )を生成し、 class_eval で 環境ごとのメソッド(development?, test?, production?)を作成しています。

#https://github.com/rails/rails/blob/v7.1.1/activesupport/lib/active_support/environment_inquirer.rb
module ActiveSupport
  class EnvironmentInquirer < StringInquirer # :nodoc:
    DEFAULT_ENVIRONMENTS = %w[ development test production ]

    def initialize(env)
      raise(ArgumentError, "'local' is a reserved environment name") if env == "local"

      super(env)

      DEFAULT_ENVIRONMENTS.each do |default|
        instance_variable_set :"@#{default}", env == default
      end

      @local = in? LOCAL_ENVIRONMENTS
    end

    DEFAULT_ENVIRONMENTS.each do |env|
      class_eval <<~RUBY, __FILE__, __LINE__ + 1
        def #{env}?
          @#{env}
        end
      RUBY
    end

こういう仕組みになっていたわけですね。

Rails.envはこのクラスのオブジェクトです。

>> Rails.env.class
=> ActiveSupport::EnvironmentInquirer

String

最初に説明した、 ActiveSupport::StringInquirer をnewする inquiry を用意しています。

# https://github.com/rails/rails/blob/v7.1.1/activesupport/lib/active_support/core_ext/string/inquiry.rb
class String
  def inquiry
    ActiveSupport::StringInquirer.new(self)
  end

1冊ですべて身につくHTML & CSSとWebデザイン入門講座 読了

HTMLもCSSも全然分からんという若者のために会社で購入。
面白そうだったので読んでみました。
前半は本当にweb分からん人向けの内容で、かなり親切に書かれています。
個人的には、CSSグリッドあたりはあまり知らなかったので、 grid-template-columns とか背景画像のブレンドモード(backgroundblend-mode)辺りは、へーという感じでした。
全体的に丁寧かつ分かりやすく書かれていて、駆け出しWebデザイナーとかコーダーさんとかには良さそうでした。