環境変数などの設定が必要なHomebrew packageをEnvironment Modulesで管理する
はじめに
macOSでHomebrewを使っている方はpackageを追加した際に以下のような表示を見たことがあるのではないでしょうか?
llvm is keg-only, which means it was not symlinked into /opt/homebrew,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.
書いてある通りですが, macOSや他のformulaが提供しているsoftwareと競合するpackageをHomebrewでinstallしようとするとsymbolic linkは作成されません.
そのため, keg-onlyのpackageを使用するには手動で環境変数などの設定を行う必要があります
[1].
どうやって設定を行えば良いかというと基本的にpackageをinstallした際に表示されるmessageに書いてあります.
例えば, llvmの場合は以下のようになっています
llvmはC(++) compilerのLLVM Clangのことです.
正確にはllvmを入れるとLLVM Clang以外も色々入る気がしますが, ややこしいので以下ではLLVM Clangという呼び方をします.
To use the bundled libc++ please add the following LDFLAGS:
LDFLAGS="-L/opt/homebrew/opt/llvm/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm/lib/c++"
llvm is keg-only, ... # 上記のmessageのため省略
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"
まとめると環境変数PATH
, LDFLAGS
, CPPFLAGS
を設定せよということです.
基本的に書いてあるcommandをterminalで実行すれば動きますが, 上記commandには2つ問題点があります.
1つ目はPATH
変数の設定です.
上記のmessageの該当部分は
If you need to have llvm first in your PATH, run:
echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc
で, .zshrc
でPATH
変数を設定するように書かれています.
このcommandを実行するとterminalの起動時に自動的にLLVM Clangが使用可能になります.
この方法の問題は, macOSに入っているApple Clang[2]が見つからなくなることです.
自分でApple Clangを使いたい場合や, 他のpackageなどがApple Clangを前提としている場合は不具合が発生する可能性があります.
2つ目は環境変数などを毎回設定するのが面倒ということです.
上記のcommandを覚えたり, どこかに記録しておかないといけません.
また, 上記のmessageはinstallした際にしか表示されないので1度忘れると再確認するのも面倒です.brew info llvm
で再表示可能でした.
これらの問題を解決するためにEnvironment Modulesを用いて環境変数などの設定を管理します.
Environment Modulesは環境変数などを管理するためのpackageで, 共同利用スパコンでよく使われています.
Environment Modulesを導入することで
module load llvm
というcommandを実行するだけでPATH
, CPPFLAGS
, LDFLAGS
が設定されるようになります.
また,
module unload llvm
で元に戻すことができ, Apple Clangとの使い分けも容易になります.
この後は
- Environment Modulesでpackageを管理するための
modulefile
の作成 - Environment Modules packageの導入
という流れになります.
今回はllvm (LLVM Clang)を例にして説明します.
内容としては通常のEnvironment Modulesの導入と大差はありませんが, Homebrew+Environment Modulesの設定の具体例として参考にしていただければと思います.
keg-only packageの管理方法として他に良い方法があれば教えていただきたいです.
modulefile
の作成
まずはEnvironment Modulesで各packageを管理するためのmodulefile
を作成します.
modulefile
を置いておくdirectoryを決めてください.
ここではMODULEFILE_PATH
とします.
MODULEFILE_PATH
にmodulefiles
というdirectoryを作成して, llvm
という設定fileを以下のように作成します.
#%Module
conflict llvm
set root "$::env(HOMEBREW_PREFIX)/opt/llvm"
# set PATH
prepend-path PATH "$root/bin"
# set CPPFLAGS
if { [info exists ::env(CPPFLAGS)] } {
setenv CPPFLAGS "-I$root/include $::env(CPPFLAGS)"
} else {
setenv CPPFLAGS "-I$root/include"
}
# set LDFLAGS
if { [info exists ::env(LDFLAGS)] } {
#setenv LDFLAGS "-L$root/lib $::env(LDFLAGS)"
# To use the bundled libc++
setenv LDFLAGS "-L$root/lib -L$root/lib/c++ -Wl,-rpath,$root/lib/c++ $::env(LDFLAGS)"
} else {
#setenv LDFLAGS "-L$root/lib"
# To use the bundled libc++
setenv LDFLAGS "-L$root/lib -L$root/lib/c++ -Wl,-rpath,$root/lib/c++"
}
modulefile
はTcl言語で記述されます.
詳細については述べませんが簡単に記載内容について説明します.
-
modulefile
のfile名は任意です. ここではllvm
としていますがversion名など他の名前でも問題ありません. 実際に使用する際にはmodule load llvm
というようにmodulefile
のfile名を指定するため, 命名規則を明確にしておいた方が良いでしょう. -
#%Module
はmodulefile
の先頭に必ず書く必要があります. -
conflict llvm
は同時にloadできないmoduleを指定するための設定です
[3]. -
set root
でroot
というmodulefile
内で用いる変数を設定しています.HOMEBREW_PREFIX
(AArch64版Homebrewの場合のdefaultは/opt/homebrew
)はHomebrewによってinstallされたpackageのroot directoryの場所を指定する環境変数です.これを$::env(HOMEBREW_PREFIX)
として取得しroot
に代入しています. -
PATH
はprepend-path
で設定します.prepend-path
は現在のPATH
変数の先頭に追加します. -
append-path
で現在のPATH
の末尾に追加することもできます. - 環境変数は
setenv
で設定します. -
setenv
はprepend-path
やappend-path
と異なり現在の環境変数を上書きします.
そのため, 環境変数が既に設定されているか否かを判定し, 動作を変えるようにしています. -
LDFLAGS
の設定が少し複雑なのは, LLVMの標準librarylibc++
を使う設定をしているためです. Apple Clangにbundleされたlibc++
を使用して問題ない場合はcomment outしている部分の設定を代わりに使ってください.
他のpackageについても同様にmodulefile
を作成することでEnvironment Modulesで管理できるようになります.
Environment Modulesの導入
Environment Modulesを導入します.
Homebrewで入ります:
brew install modules
次に, module
commandを使用するために.zshrc
に以下を追記します[4].
MODULEFILE_PATH
はmodulefiles
を置くdirectoryのpathを記載してください.
1行目に関してはEnvironment Modulesをinstallした際に出てくるmessageに書いてあるcommandを参照した方が確実です.
source $HOMEBREW_PREFIX/opt/modules/init/zsh
module use /MODULEFILE_PATH/modulefiles
追加したらterminalを再起動するかsource ~/.zshrc
を実行して設定を反映させます.
module
commandが使えるか確認します.
module --version
Modules Release 5.3.1 (2023-06-27) #出力結果
これでEnvironment Modulesの導入は完了です.
先に記載した通り,
module load llvm
でLLVM Clangが使えるようになります.
module
commandの詳しい使い方は公式documentなどを参照してください.
参考文献
-
なお, keg-onlyというのは直訳すると「樽だけ」となります.
Homebrew(自家酒造)ではCellar(地下貯蔵室) directoryでpackageが管理されているため, keg-only(樽だけ)というのはCellar内に留めて外に出さないということなのだと思います.
知らんがな. ↩︎ -
正確にはXcodeかCommand Line Tools for Xcodeを入れる必要がありますが, Homebrewを入れる際に必要なので入っているはずです. ↩︎
-
これは, 例えばLLVM Clangの異なる複数のversionを管理する場合に, 異なるversionのLLVM Clangを同時にloadすることができないようにするというような使い方をします.
ここではmodulefile
のfile名と同じ名前をconflict
で指定しているので少し紛らわしいですが,conflict
で指定する名前はmodulefile
のfile名とは関係なく,conflict
で指定した名前で衝突判定が行われます. ↩︎ -
bashなど, 他の環境を使っている方は適宜読み替えてください.
source
commandの内容も変わるはずなので, Environment Modulesをinstallした際に表示されるmessageを確認してください ↩︎
Discussion