みずぴー日記

人間の再起動ボタンはハワイのビーチにある

🔀Rust クロスコンパイル

Docker for Mac内のRustでmacOS向けのプログラムを書けるようにした。

⭐️要約

以下のDockerfireでクロスコンパイルのできるイメージを作成できる。

FROM multiarch/crossbuild

# reinstall osx cross compiler to link with SDK 10.7
ENV DARWIN_OSX_VERSION_MIN="10.7"
RUN rm -rf /usr/osxcross \
 && mkdir -p "/tmp/osxcross"                                                                                   \
 && cd "/tmp/osxcross"                                                                                         \
 && curl -sLo osxcross.tar.gz "https://codeload.github.com/${OSXCROSS_REPO}/tar.gz/${OSXCROSS_REVISION}"  \
 && tar --strip=1 -xzf osxcross.tar.gz                                                                         \
 && rm -f osxcross.tar.gz                                                                                      \
 && curl -sLo tarballs/MacOSX${DARWIN_SDK_VERSION}.sdk.tar.xz                                                  \
             "${DARWIN_SDK_URL}"                \
 && yes "" | SDK_VERSION="${DARWIN_SDK_VERSION}" OSX_VERSION_MIN="${DARWIN_OSX_VERSION_MIN}" ./build.sh                               \
 && mv target /usr/osxcross                                                                                    \
 && mv tools /usr/osxcross/                                                                                    \
 && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/omp                                                    \
 && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/osxcross-macports                                      \
 && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/osxcross-mp                                            \
 && rm -rf /tmp/osxcross                                                                                       \
 && rm -rf "/usr/osxcross/SDK/MacOSX${DARWIN_SDK_VERSION}.sdk/usr/share/man"

# install toolchain
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y

ENV PATH=/root/.cargo/bin:$PATH

# install target
RUN rustup target add x86_64-apple-darwin

WORKDIR /work

カレントディレクトリに hello.rs を配置した上で、以下のコマンドを実行すると、macOS向けのバイナリが生成される。

docker build -t rust-cross-compile .
docker run -v $PWD:/work -it -e CROSS_TRIPLE=x86_64-apple-darwin rust-cross-compile rustc --target=x86_64-apple-darwin hello.rs

📝調べたこと

上記のDockerfileを書くまでに調べたことをメモしておく。

標準ライブラリ

macOS向けにビルドされた標準ライブラリをインストールする。 これは rustup から行なえる。

$ rustup target add x86_64-apple-darwin
info: downloading component 'rust-std' for 'x86_64-apple-darwin'
info: installing component 'rust-std' for 'x86_64-apple-darwin'

リンカ

リンカがMach-O形式を理解しないので、ビルドできずエラーになる。

$ rustc --target=x86_64-apple-darwin hello.rs
error: linking with `cc` failed: exit code: 1
  |
  (snip)
  = note: hello.0.o: file not recognized: File format not recognized
          collect2: error: ld returned 1 exit status


error: aborting due to previous error

multiarch/crossbuildが各ターゲットのツールチェインを含んでいるのでこれを使うようにする。 このイメージには crossbuild というツールチェインを切り替えるシェルスクリプトが含まれているので、これを使うようにする。

CROSS_TRIPLE=x86_64-apple-darwin crossbuild rustc --target=x86_64-apple-darwin hello.rs

macOS向けの設定

標準ライブラリは OSX 10.7 向けにビルドされているが、multiarch/crossbuild に含まれるリンカは 10.6向けのものなので、うまくビルドできない。

CROSS_TRIPLE=x86_64-apple-darwin crossbuild rustc --target=x86_64-apple-darwin hello.rs
error: linking with `cc` failed: exit code: 1
  |
  (snip)
          ld: warning: object file (...) was built for newer OSX version (10.7) than being linked (10.6)
          ld: targeted OS version does not support use of thread local variables in __ZN4core6result13unwrap_failed17h374a94f6b7a942a9E for architecture x86_64
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
          

error: aborting due to previous error

そのため、macOS向けのツールチェインを再ビルドする。

export DARWIN_OSX_VERSION_MIN="10.7"
rm -rf /usr/osxcross \
 && mkdir -p "/tmp/osxcross"                                                                                   \
 && cd "/tmp/osxcross"                                                                                         \
 && curl -sLo osxcross.tar.gz "https://codeload.github.com/${OSXCROSS_REPO}/tar.gz/${OSXCROSS_REVISION}"  \
 && tar --strip=1 -xzf osxcross.tar.gz                                                                         \
 && rm -f osxcross.tar.gz                                                                                      \
 && curl -sLo tarballs/MacOSX${DARWIN_SDK_VERSION}.sdk.tar.xz                                                  \
             "${DARWIN_SDK_URL}"                \
 && yes "" | SDK_VERSION="${DARWIN_SDK_VERSION}" OSX_VERSION_MIN="${DARWIN_OSX_VERSION_MIN}" ./build.sh                               \
 && mv target /usr/osxcross                                                                                    \
 && mv tools /usr/osxcross/                                                                                    \
 && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/omp                                                    \
 && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/osxcross-macports                                      \
 && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/osxcross-mp                                            \
 && rm -rf /tmp/osxcross                                                                                       \
 && rm -rf "/usr/osxcross/SDK/MacOSX${DARWIN_SDK_VERSION}.sdk/usr/share/man"

ビルド

これでビルドできるる。

$ CROSS_TRIPLE=x86_64-apple-darwin crossbuild rustc --target=x86_64-apple-darwin hello.rs
$ file hello
hello: Mach-O 64-bit x86_64 executable