Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Кросс сборка

Разные платформы

Когда вы собираете программу на Rust, то по умолчанию она компилируется в машинный код для той платформы, на которой вы работаете, и линкуется с той стандартной библиотекой C, которая у вас установлена. Поэтому если вы работаете на Windows или MacOS, то у вас не получится просто собрать бинарный исполняемый файл, скопировать его на Linux сервер и там его запустить.

Также если вы работаете под Linux, но версия GLIBC на вашей системе старше, чем версия GLIBC на целевом Linux сервере, то скорее всего, исполняемый бинарный файл, собранный под вашу систему, также не запустится на целевом Linux сервере.

Tip

GLIBC — стандартная библиотека C, используемая по умолчанию в большинстве дистрибутивов Linux.

Собрать программу на Rust под другую платформу можно двумя путями:

  • Произвести сборку на таком же окружении, как и целевая платформа. Это может быть как отдельная физическая машина, так и Docker контейнер или виртуальная машина, где запущена такое же окружение, как и на целевой платформе.
  • Произвести кросс-компиляцию под другую целевую платформу.

Со сборкой на другой машине, или в Docker контейнере всё более-менее понятно. А с кросс-компиляцией мы будем разбираться дальше в этой главе.

Проблемы кросс-компиляции

Для того чтобы собрать Rust программу под другую платформу, надо сделать две вещи:

  • скомпилировать исходный код в объектный файл (машинный код в определённом формате) для целевой платформы
  • слинковать объектные файлы со стандартной библиотекой C целевой платформы

Если скомпилировать код в объектные файлы для другой платформы легко (надо просто указать соответствующую опцию компиляции), то для линковки со стандартной библиотекой C под другую платформу всё сложнее, ведь вам понадобятся:

  • линкер, умеющий собирать исполняемые файлы целевой платформы
  • библиотека C от целевой платформы

Например, собрать программу для Windows x86_64 из под Linux Ubunu относительно нетрудно, но только потому, что в репозитории пакетов Ubuntu имеется нужный пакет с библиотекой C и линкером, который умеет собирать исполняемые файлы для Windows:

# Установить MinGW C++ тулчейн  для Windows, который содержит линкер и бибилотеку C
sudo apt install gcc-mingw-w64-x86-64
# Установить Rust тулчейн для кросс компиляции под Windows x86_64
rustup target add x86_64-pc-windows-gnu
# Собрать приложение для Windows x86_64 с библиотекой C из MinGW w64
cargo build --release --target x86_64-pc-windows-gnu

А вот собрать программу из под Windows для Linux уже сложнее: скомпилировать объектные файлы также легко, а добыть линкер и GLIBC нужной версии — проблема.

Zig-build

В этом месте надо сделать отступление и сказать пару слов о таком языке программирования, как Zig. Это компилируемый язык, который имеет такие же правила кросс-платформенной сборки: ему необходимы линкер и стандартная библиотека C под целевую платформу. Однако особенность Zig заключается в том, что Zig SDK содержит в себе и линкеры, и стандартные библиотеки C сразу под все основные платформы. Таким образом, для кросс-сборки программы на Zig нет необходимости дополнительно что-то устанавливать, ведь Zig SDK уже содержит всё необходимое.

Такая особенность Zig не могла остаться незамеченной членами Rust сообщества, которые вскоре создали утилиту zigbuild. Эта утилита использует линкер и библиотеку C из Zig SDK для кросс-сборки программ на Rust.


Для начала установим Zig SDK:

  1. Скачайте последнюю стабильную версию Zig SDK c официального сайта:
    https://ziglang.org/download/. Результатом скачивания должен быть tar.xz или zip архив с именем наподобии zig-x86_64-linux-0.14.1.tar.xz.
  2. Распакуйте архив в удобное для вас место.
  3. Добавьте путь к корневой папке с Zig SDK (папка в которой находится исполняемый файл zig) в системный путь. На Unix-подобных системах — это переменная PATH, на Windows — Path.

Теперь установите утилиту zigbuild. Для этого выполните команду:

cargo install --locked cargo-zigbuild

Warning

Если у вас Windows и вы используете MinGW, а не Visual C++, то при сборке zigbuild у вас могут возникнуть проблемы с линковкой с системными библиотеками Windows. В этой ситуации рекомендуется просто установить Visual C++, или воспользоваться Docker образом с zigbuild.

Сборка для Windows

Предположим, что вы работаете на Linux или MacOS, и хотите собрать программу для Windows при помощи zigbuild. Вам нужно:

1) Установить Rust тулчейн для Windows

rustup target add x86_64-pc-windows-gnu

2) Собрать программу, используя zigbuild:

cargo zigbuild --release --target x86_64-pc-windows-gnu

После этого должна появиться директория target/x86_64-pc-windows-gnu/release, в которой будет содержаться исполняемый exe файл.

Сборка для Linux

Теперь рассмотрим пример, как собрать программу для Linux из-под Windows.

1) Установите тулчейн для Linux c GLIBC:

rustup target add x86_64-unknown-linux-gnu

2) Далее соберите программу:

cargo zigbuild --release --target x86_64-unknown-linux-gnu

Учтите, что GLIBC имеет разные версии. Версия GLIBC, с которой вы линкуете программу, не должна быть старше, чем версия GLIBC на системе, где вы планируете запускать вашу программу.

Чтобы узнать версию GLIBC на Linux системе, вы можете воспользоваться командой ldd:

$ ldd --version
ldd (Ubuntu GLIBC 2.39-0ubuntu8.6) 2.39
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

Zig 0.12, 0.13, 014 и 0.15 по умолчанию линкуют с GLIBC версией 2.28.

Для того чтобы при сборке задать конкретную версию GLIBC, укажите её после точки в имени таргета. Например, чтобы слинковать с GLIBC 3.34, используйте команду сборки:

cargo zigbuild --release --target x86_64-unknown-linux-gnu.3.34

Tip

Также следует заметить, что пользователи Windows могут воспользоваться WSL для сборки программ под Linux.

zigbuild Docker

Вы также можете воспользоваться готовым Docker образом zigbuild, который уже содержит Rust, Zig SDK и zigbuild.

Например, следующая команда собирает программу для Windows:

docker run --rm -it -v $(pwd):/io -w /io ghcr.io/rust-cross/cargo-zigbuild \
  cargo zigbuild --release --target x86_64-pc-windows-gnu

MUSL

Иногда вы не знаете, какая версия GLIBC будет использоваться на системе, где планируется запуск вашей программы. В некоторых дистрибутивах (например, Alpine Linux) её нет вовсе.

Для таких ситуаций существует альтернативная стандартная библиотека C — MUSL. В отличие от GLIBC, с которой программы линкуются динамически, MUSL предназначена для статической линковки. Т.е. при сборке программы машинный код библиотеки MUSL помещается непосредственно в бинарный файл программы. Таким образом отпадает необходимость во внешней динамической библиотеке C.

За сборку под Linux с MUSL отвечает отдельный Rust тулчейн, который следует установить командой:

rustup target add x86_64-unknown-linux-musl

Для сборки программы с MUSL вы можете как использовать zibguild (Zig SDK содержит MUSL), так и просто установить пакет MUSL на свою систему, если такой имеется.

Например, если вы используете Ubuntu Linux, то вам будет необходимо установить пакеты musl-tools и musl-dev:

sudo apt install musl-tools musl-dev

после чего вы сможете собрать свою программу под Linux, но слинкованную с MUSL:

cargo build --release --target x86_64-unknown-linux-musl

Но удобнее использовать zigbuild: он работает на любой системе и не требует установки дополнительных пакетов.

cargo zigbuild --target x86_64-unknown-linux-musl --release

Вы можете задаться вопросом: “Если MUSL так удобен, почему бы не использовать его для сборок под Linux всегда?”.

Если ваше приложение не использует никаких внешних динамических библиотек, то MUSL является отличным выбором по умолчанию.

Однако если ваше приложение использует динамическую библиотеку, которая слинкована с GLIBC, то сборка вашего приложения с MUSL в лучшем случае просто не даст ожидаемого результата: приложение всё равно будет нуждаться в GLIBC, только уже для внешней библиотеки.

Например, на момент написания этого текста (Rust 1.92) в экосистеме Rust отсутствовал драйвер для СУБД Oracle, который был бы полностью написан на Rust. Имеющийся крэйт oracle является просто обёрткой над библиотекой, написанной на C и слинкованной с GLIBC. Причём код библиотеки закрыт, что исключает возможность самостоятельно скомпилировать её, и статически слинковать с программой на Rust. Поэтому если ваше приложение должно работать с СУБД Oracle, вам придётся использовать GLIBC.