2023年の中旬ごろ、HomebrewがBigSurなどの古いMacOSをサポートから外してしまいました。
しばらくは「なんとかなるさ!」と使っていたのですが、半年もしないうちに重要なパッケージが更新できなくなり…
特に痛かったのは、libheif
が更新できなくなって、ImageMagic
でavifを扱えなくなったことでした。
そこで古いOSもサポートしていると噂のMacPortsに移行することに。
他の移行先としてnix
なども考えましたが、ちょっと調べた感じだと「まだ過渡期なのかな」と思ったので、無難にMacPortsにしました。
で、実際に使ってみたら、拍子抜けするぐらい、普通に移行できました。
しかし、やはり一部ハマったものもあったので、その経験を残しておきます。
ハマりポイントは2つで、自分の場合はcargo-edit
(Rust)とfabric
(Python3)でした。
cargo-editのインストール(と起動)に失敗する
MacPortsを運用してしばらくして、Rustのcargo-edit
というcrateのインストールに失敗することに気付きました。
エラーメッセージを見ると、libiconv.dyld
という動的ライブラリのリンクに失敗している模様です。
で、どうもそのリンク先が、MacPortsが管理するlibiconvっぽい…
MacPortsでlibiconvをインストールした覚えはないのですが、広く使われてる文字列変換のライブラリなので、まあなにかのついでにインストールされたのでしょう。それがリンクされて、エラーになったみたいです。
エラーになる理由は、MacPortsでインストールされるlibiconvが、世間一般に公開されている「普通の」libiconvとは、ちょっとだけ異なるから。
なんか他のエコシステムのlibiconvと衝突しないように、いくつかの関数が別名で登録されているらしいのですね。
だからこれをリンク先の動的ライブラリとして選択されてしまうと、リンクエラーになってしまうわけです。
なにが異なるのかは、
nm -a /opt/local/lib/libiconv.dylib | grep iconv
nm -a /usr/local/lib/libiconv.dylib | grep iconv
の出力結果を比べたらわかるはずですが、実際にやってみたら「アレ? 別に変わったところはないけど?」という感じでした。
でもまあ、リンクエラーが起きてるんで、なんか違うんでしょう(笑)。
そういうわけで、MacPortsの外の世界のプログラムにlibiconvをリンクするときは、MacPortsが使っている(特別仕様の)libiconvをリンクさせないようにする必要があります。
今回トラブったcargo-edit
についても、DYLD_LIBRARY_PATH
が最初に/opt/local/lib
を探す仕様になっているせいで、そのままコンパイルするとリンクエラーになるわけです。とはいえ、DYLD_LIBRARY_PATH
の上書きは、他のトラブルを起こすことが多いので避けたいところです。
これを解決する方法として、自分の環境では次の2つがうまくいきました。
1つ目は、Rust固有の環境変数を使うもの。
幸いなことに、RustにはRUSTFLAGS
という環境変数を通じて、コンパイル時のオプションを渡せる仕組みが用意されています。これを使ってリンク先を変更することができました。
.zshrcとかに、
export RUSTFLAGS="-L/usr/local/lib"
を付け足せばOKです。
もちろんBigSurでは/usr/local/lib
にlibiconvは入っていないので、ソースからコンパイルして、/usr/local/lib/
にインストールしました。
./configure --prefix=/usr/local && make && make install
2つ目の方法はもっと簡単で、cargo-edit
をインストールするときだけMacPortsのlibiconvを無効にする、という方法。
# いったん無効にする
sudo port -f deactivate libiconv
# cargo-editをインストール(システム依存のlibiconvが使われる)
cargo install cargo-edit
# 終わったら再び有効にする
sudo port activate libiconv
こうすると、MacPorts以外のlibiconvにリンクしてくれます。
自前で/usr/local/lib
などにインストールしてない状態でもうまくいったので、BigSurのどっかしらに、普通のlibiconvがあるんでしょう(笑)。
これでcargo-edit
はインストールできました。
ただ自分の環境では、これで終わりではなく、その後にcargo-edit系のコマンド(upgrade, add, rm, set-version)を実行したら、また次のようなエラーが出ました。
~/.cargo/registry/index/github.com-1ecc6299db9ec823 is unusable due to having an invalid HEAD reference: reference 'refs/heads/main' not found; class=Reference (4); code=NotFound (-3)
なんかcargo-editに関するgithubのレポジトリ情報が~/.cargo
以下に適切に配置されていない、みたいなエラーです。
最近のcargoは、パッケージを管理するcrates-io
というサーバーとの通信に、sparse
というプロトコルを使うようになったらしいのですが、cargo-editをインストールする際にもこのプロトコルが使われたことで、githubのrepositoryが~/.cargo/registry
以下に適切に登録されなかったっぽいです。
以前のcargoは、巨大なリポジトリをgitプロトコルでローカルに引っ張ってくる仕組みで、その際レポジトリの構造がローカル環境にも配置されていたのでしょう。
しかしcargoのプロトコルの刷新で、それがなくなったことにより、このエラーが出たのだと思います。
エラーを見る限りcargo-edit
はローカルにgit-repositoryが配置されてないと動かないらしいので、最初にcargo install cargo-edit
するときだけは、gitプロトコルでcrates-io
と通信するようにします。
これも2つの解決方法があって、1つはCargo.tomlに
[registries.crates-io]
protocol = "git"
#protocol = "sparse"
と書く方法。もちろん終わったら"sparse"側を有効にします。
もう一つは環境変数CARGO_REGISTRIES_CRATES_IO_PROTOCOL
をセットする方法で、.zshrcなどに
export CARGO_REGISTRIES_CRATES_IO_PROTOCOL="git"
と書いておきます。
こうした上で、cargo uninstall cargo-edit
してから、cargo install cargo-edit
すると、正常にインストール&起動するようになりました。
cargo-edit
ほど大変ではありませんでしたが、fabric
もすんなりとは動きませんでした。
最初はMacPortsをfabric
で検索したら、pythonのそれぞれのバージョンごとに用意されていることがわかったので、素直にそれをインストールしました。
しかし実際に使ってみると、いくつかの処理がエラーで動きませんでした。
そこでpython3
とpip3
はMacPortsでインストールし、fabric
だけpip3
でインストールするようにしたら上手くいきました。原因はわかりません(笑)。
ちなみにMacPorts経由でインストールしたpip
でインストールするfab
コマンドは、/opt/local/Library/Frameworks/Python.framework/Versions/3.xx/bin/
にインストールされるので、sudoで実行する必要があります。本当はvenvとか使ったほうが良いんでしょうが、面倒なのでスキップしました。
# python3とpip3をインストール
sudo port install py311 pip-3.11
# pip, pip3ともにpip-3.11を使用
sudo port select --set pip pip-3.11
sudo port select --set pip3 pip-3.11
# Macportsのpipでfabricをインストール(sudoに注意)
sudo pip install fabric