rust
xargo

xargoによるRustのクロスコンパイル

これはRust Internal Advent Calender 12/17の記事です

xargo: The sysroot manager that lets you build and customize std

標準ライブラリと#![no_std]

xargoの説明をする前にRustの標準ライブラリについて確認しましょう。Rustにはcorestdの名前空間があります。stdが通常の標準ライブラリ(libstd)を担うのに対し、coreはRust言語のための最小の標準ライブラリ(libcore)を提供します。
http://ykomatsu.github.io/rust/book-ja/no-stdlib.html

#![no_std]属性を付けることによりstdを使用せず、代わりにcoreを使用します。libcoreはlibstdよりも小さく依存関係も少ないため、libstdすべてが実装されていないなどの場合に使えます。Rust Platform Supportにlibstd/rustc/cargoの対応状況がまとめられています。現状stdが未対応なのはthumb*-none-eabiと特殊なターゲットであるnvptxだけです(例によってAccelの開発のための調査です)。
xargoはこのlibstdやlibcoreをカスタマイズしてコンパイルするためにユーティリティです。

Target Triple / sysroot

Rustがベースとしているコンパイル基盤LLVMにはTarget Tripleというアーキテクチャの指定方式があります。

The triple has the general format <arch><sub>-<vendor>-<sys>-<abi>, where:
arch = x86_64, i386, arm, thumb, mips, etc.
sub = for ex. on ARM: v5, v6m, v7a, v7m, etc.
vendor = pc, apple, nvidia, ibm, etc.
sys = none, linux, win32, darwin, cuda, etc.
abi = eabi, gnu, android, macho, elf, etc.

https://clang.llvm.org/docs/CrossCompilation.html

RustはバックエンドへのコンパイルをLLVMに頼っているため、アーキテクチャの指定はこれをそのまま使用します。

xargoはcargo互換のビルドコマンドですが、更にsysroot管理機能があります。クロスコンパイルではそのターゲットに応じてリンクするライブラリを切り替える必要がありますが、ホスト側のlibcoreとターゲットのlibcoreがあってややこしいことになります。sysrootはこの管理を簡単にするため、仮想的なroot directoryを用意してそこ以下のlib/等を使います。xargoはこのディレクトリの管理とホストとターゲットの切り替えを実施してくれます。
この記事を書いてる段階では$HOME/.xargo以下にターゲット毎にlibcore等が用意されます。

xargoの使い方

READMEに詳しく書いてます!(`・ω・´)キリッ
https://github.com/japaric/xargo

なので要点だけ短めに・・・
xargoはcargoのコマンドがそのまま使えます。buildだけでなくpublish等も使えるのでターゲット依存のコードが入っていてもアップロードできます。クロスコンパイル時は--targetでターゲットを指定します:

xargo build --target thumbv6m-none-eabi

xargoはcargoと違ってデフォルトでlibstd ではなく libcoreをリンクします。
libstdをリンクするにはXargo.tomlファイルを作成する必要があります:

Xargo.toml
[target.thumbv6m-none-eabi.dependencies]
collections = {}

これでlibcoreに加えて[libcollection]がリンクされます。

Xargo.toml
[target.i686-unknown-linux-gnu.dependencies.std]
features = ["jemalloc"]

これはi686-unknown-linux-gnuターゲットの場合にlibstdをjemallocオプションでコンパイルします。このようにクロスコンパイルではなく、カスタムなstdを作成するのにもxargoを使用できます。

カスタムターゲット

Rust Platform Supportにあるターゲットに対しては上で十分ですが、nvptxや他のサポートされていないターゲットに対しても、自分でターゲットを記述することによってクロスコンパイルすることが可能です。nvptxについてはjaparic/nvptxの方に詳しい記述があります:1

nvptx64-nvidia-cuda.json
{
    "arch": "nvptx64",
    "cpu": "sm_20",
    "data-layout": "e-i64:64-v16:16-v32:32-n16:32:64",
    "linker": "false",
    "linker-flavor": "ld",
    "llvm-target": "nvptx64-nvidia-cuda",
    "max-atomic-width": 0,
    "os": "cuda",
    "panic-strategy": "abort",
    "target-c-int-width": "32",
    "target-endian": "little",
    "target-pointer-width": "64"
}

これをXargo.tomlの横に置くことで以下が実行できます:

xargo build --target nvptx64-nvidia-cuda

nvptxはstdが対応していないのでlibcoreを使うことになります。この時使うlibcoreを指定することもできます2

Xargo.toml
[dependencies.core]
git = "https://github.com/japaric/core64"
rev = "0e29675fda37afa727f96818dabd8cbfb969dba4"

最後に

皆さんもクリスマスプレゼントとして新しいアーキテクチャを手に入れたと思いますので、是非この機会にRustのクロスコンパイルをお試しください(`・ω・´)


  1. あるいはdenzp/rust-ptx-linkerの改良版を使う方がいいかもしれません 

  2. termoshtt/accel:src/ptx_builder/Xargo.toml