LoginSignup
1

More than 3 years have passed since last update.

macOS Catalina で ASDF を使って Erlang 20 をインストール

Last updated at Posted at 2019-10-14

一筋縄ではいかなかったので記事にしてみます。
Catalina を使い始めた方の助けになればと思います。

まず、何をしようとしていたかを書いておきます。

  • Erlang 20.3.8.9 を macOS Catalina にインストールしたい。
    • 古いですね。。
  • asdf-erlang を使ってインストールしたい。

私がとった方法はまとめに書いてあります。

ソースから Erlang をコンパイルしてみる

問題の切り分けのために、まず ASDF を使わずに Erlang のコンパイルをしてみました。
このとき生じた問題を記録しておきます。

Stack check のデフォルト有効化

この問題は Erlang/OTP PR#2413 によって対処済です。
詳しくは分かりませんが、コンパイル時の stack check がデフォルトで有効化されたことにより、clang の潜在的なバグが顕在化してしまったようです。
したがって、CFLAGS-fno-stack-check を含めればコンパイルエラーを回避できます。

Erlang 22.1.4 で反映されるようなので、それよりも古い Erlang を使いたい場合は、-fno-stack-check を含めた CFLAGS を自分で設定してコンパイルすれば大丈夫です。
デフォルトの CFLAGS-O2 -g らしいので、私は下記のようにしました。

export CFLAGS="-O2 -g -fno-stack-check"

OpenSSL が見つからない

この問題は Catalina にする前からあったのかもしれないです。。
原因はここに書いてあります。

Configure するときに明示的に OpenSSL の場所を指定すれば大丈夫です。
私は下記のようにして Homebrew でインストールした OpenSSL を指定しました。

./configure --with-ssl=/usr/local/opt/openssl

Super Carrier がつくれない

この問題が最も大変でした。
症状としては、コンパイル時に下記のエラーが起きます。

=== Entering application hipe
 VSN  hipe.hrl
 ERLC ../ebin/hipe_rtl.beam
erts_mmap: Failed to create super carrier of size 512 MB
make[3]: *** [../ebin/hipe_rtl.beam] Error 1
make[2]: *** [opt] Error 2
make[1]: *** [opt] Error 2
make: *** [secondary_bootstrap_build] Error 2

Super Carrier とは

Failed to create super carrier とありますが、Super Carrier は何でしょうか?
ここに書いてありました。
パフォーマンス向上のために Erlang VM の起動時にある程度の大きさのメモリ領域を確保しておくらしいのですが、そのメモリ領域のことを Super Carrier を呼ぶそうです。

HiPE とは

Entering application hipe とありますが、HiPE とは何でしょうか?
この記事が分かりやすかったです。
Erlang のソースコードをネイティブコードとしてコンパイルしてくれる機能のようです。

そして、コンパイラされたものは Super Carrier に配置されるようです。

32 bit のための配慮が仇となっていた

この問題の原因は、HiPE の生成物が所謂 low 2GB address space に配置されるように、プログラムがこのメモリ領域に Super Carrier をつくろうとしていることです。
よく知りませんが、32 bit システムではこのメモリ領域を使わざるを得ないみたいですね。。

Super Carrier と HiPE に関するコード(のひとつ)は Erlang/OTP リポジトリの erts/emulator/sys/common/erl_mmap.c にあります。
ここで下記のような関数が定義されています(下記はリポジトリではなく、20.3.8.9 のソースから抜き出しました)。

static void *
os_mmap_virtual(char *ptr, UWord size, int exec)
{
    int flags = ERTS_MMAP_VIRTUAL_FLAGS;
    void* res;

#ifdef ERTS_ALC_A_EXEC
    if (exec) {
        ASSERT(!ptr);
        /* OTP-19.0: Nice hack below cut-and-pasted from hipe_amd64.c */

#  ifdef MAP_32BIT
        /* If we got MAP_32BIT (Linux), then use that to ask for low memory */
        flags |= MAP_32BIT;
#  else
        /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect
           a plain map_hint (returns high mappings even though the
           hint refers to a free area), so we have to use both map_hint
           and MAP_FIXED to get addresses below the 2GB boundary.
           This is even worse than the Linux/ppc64 case.
           Similarly, Solaris 10 doesn't have MAP_32BIT,
           and it doesn't respect a plain map_hint. */
        ptr = (char*)(512*1024*1024); /* 0.5GB */

#    if defined(__FreeBSD__) || defined(__sun__)
        flags |= MAP_FIXED;
#    endif
#  endif /* !MAP_32BIT */
    }
#else /* !ERTS_ALC_A_EXEC */
    ASSERT(!exec);
#endif
    res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT,
               flags, ERTS_MMAP_FD, 0);
    if (res == (void *) MAP_FAILED)
  return NULL;
    return res;
}

macOS は MAP_32BIT を持たないため、ptr = (char*)(512*1024*1024); が実行されることになります。
FreeBSD doesn't have MAP_32BIT ... で始まるコメントにあるように、below the 2GB boundary なメモリ領域を確保するために、関数 mmap() に渡すメモリ領域の開始位置 ptr を 0.5GB に固定しています。

Catalina で、プログラムが low 2GB address space にメモリが確保されるように指定することができなくなったのか、それとも low 2GB address space に限らずメモリ確保の指定ができなくなったのかは分かりませんが、とにかく、MAP_32BIT を使った条件分岐部分を削除すれば、コンパイルが通るようになります。
ちなみに、ソースコードには os_mmap_virtual() の第一引数 ptrNULL の場合があり、したがって mmap() に渡される ptrNULL になる場合があるのですが、このとき確保されるメモリ領域の開始位置はシステムが決めてくれるみたいです。

なお、MAP_32BIT を使った条件分岐部分の削除は Erlang/OTP の PR#1699 で反映されており、Erlang 21 以降であればこの問題は起こりません
リリースノートには下記のように書いてあります。

 OTP-14951    Application(s): erts

              Removed need for HiPE to allocate native executable
              memory in low 2GB address space on x86_64. Command line
              option +MXscs is thereby obsolete and ignored.

Catalina では 32 bit アプリが使えなくなるという話を聞きますが、この問題もそれに関連した話なのかもしれませんね。。

kerl を使って Erlang をコンパイルしてみる

asdf-erlang は内部的に kerl を使っています。
問題の切り分けのために、直接 kerl を使って Erlang をコンパイルしてみました。
ソースコードを変更することができないため、HiPE 自体を無効化するしかありませんでした。。

export KERL_CONFIGURE_OPTIONS="--disable-hipe --with-ssl=/usr/local/opt/openssl"
./kerl build 20.3.8

もしかしたらコンパイルオプションで問題を回避する方法もあるのかもしれませんが、調べきれませんでした。。

ASDF を使って Erlang をコンパイルしてみる

asdf-erlang を使う場合も KERL_CONFIGURE_OPTIONS が効くはずです。
しかし、KERL_CONFIGURE_OPTIONS の設定を二通り試してみたのですが、設定が反映されていないようでした。

  • export する。
  • ~/.kerlrc に書く。

前者の原因はよく分かっていませんが、後者の理由は、asdf-erlang が実際に読みに行く設定ファイルが ~/.asdf/plugins/erlang/kerl-home/.kerlrc であるためです。
したがって、下記のようにして KERL_CONFIGURE_OPTIONS の設定を反映させることができました。

mkdir -p ~/.asdf/plugins/erlang/kerl-home
echo 'KERL_CONFIGURE_OPTIONS="--disable-hipe --with-ssl=/usr/local/opt/openssl"' > ~/.asdf/plugins/erlang/kerl-home/.kerlrc

ちなみに、この問題が kerl の Issue#320 と関係しているかな?と思ったのですが、まだ詳しく調べきれていません。

まとめ

まず、Catalina で ASDF を使って Erlang 20 をインストールしたい場合、HiPE を諦めざるを得ませんでした(少なくとも私の力量では)。
このことを理解した上で、私は下記の方法でインストールしました。

mkdir -p ~/.asdf/plugins/erlang/kerl-home
echo 'KERL_CONFIGURE_OPTIONS="--disable-hipe --with-ssl=/usr/local/opt/openssl"' > ~/.asdf/plugins/erlang/kerl-home/.kerlrc
export CFLAGS="-O2 -g -fno-stack-check"
asdf install erlang 20.3.8.9
# お掃除
unset CFLAGS
rm ~/.asdf/plugins/erlang/kerl-home/.kerlrc

インストールした Erlang は(使っているのは Elixir ですが)今のところ問題なく動作しています。
また、Mojave のときにインストールした Erlang も今のところ問題なく Catalina で動作しています。

ですが、可能であれば、Erlang 21 に移行したほうが良さそうです。。

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
1