Кросс-компиляция проекта в Docker используя Buildx на примере сборки shadowsocks-rust и библиотеки Libgpiod

Кросс-компиляция позволяет получать исполняемый код для платформы, отличной от той, на которой запускается этот процесс. В данном случае рассмотрим кросс-компиляцию в Linux клиента shadowsocks-rust в исполнение службы Windows, соответственно для платформы Windows. Процесс сборки выполним в Docker контейнере. Инструмент Buildx позволяет задавать формат получения результата сборки контейнера, это либо контейнер, либо какие-то файлы полученные путем сборки контейнера. В текущем варианте это будут исполняемые бинарные файлы для конечных аппаратных платформ и операционных систем. Сборку библиотеки Libgpiod выполним с использованием QEMU для аппаратных платформ, таких как x86, ARM и RISC-V.

Постановка задачи

Собрать клиента shadowsocks сервера для ОС Windows, исполняемый файл sswinservice.exe, проект GitHub shadowsocks-rust. Далее, собрать библиотеку Libgpiod для для аппаратных платформ таких как x86/ARM32/ARM64/RISC-V. Сборка должна выполняться в Docker контейнере инструментом Buildx, результат сборки (бинарные исполняемые файлы) определяется параметром  —output type=local,dest=out/  при создание Docker образа.

Подготовка к сборке

Сборка проектов выполняется под ОС Ubuntu 22.04.2 LTS (Jammy Jellyfish). Установка Docker просто и быстро выполняется по руководству Простая установка Docker в Armbian/Linux для ARM, руководство подойдет и для платформы x86. Для выполнения сборки необходимо установить образ QEMU для инструмента Buildx, публикация Сборка Docker контейнеров для ARM и RISC-V архитектуры используя Buildx.

Кросс-компиляция sswinservice.exe проекта shadowsocks-rust

Проект Shadowsocks-rust написан на языке Rust, компилятор которого поддерживает кросс-компиляцию. Команда разработчиков собирает исполняемые файлы для платформ Linux, Windows. При создание корпоративной сети VPN на базе решений SoftEther VPN/WireGuard с Shadowsocks-туннелированием выяснилось, что разработчики не собирают клиента shadowsocks сервера как Служба Windows, приложение sswinservice.exe. Авторы предлагают собирать sswinservice.exe самостоятельно.

Собираемый клиент  sslocal.exe  разработчиками, не работает в фоновом режиме в Windows и создает консольное окно, что несколько неудобно для постоянной работы. Поэтому потребовалось собирать sswinservice.exe, в исполнение Службы Windows.

Сборку sswinservice.exe выполним в Docker контейнере. В качестве основы инструкции сборки Dockerfile.alpine.sswinservice взят файл  GitHub shadowsocks-rust/Dockerfile.

Для линковки проекта используется кросс- и нативно- инструмент GCC toolchains4 (musl.cc) на базе musl. В зависимости от архитектуры исполнения с musl.cc загружается соответствующий архив, например для архитектуры arm64 загружается архив с названием aarch64-linux-musl-cross.tgz. Но в нашем случае необходим вариант для ОС Windows. Для Windows предназначено два архива: x86_64-w64-mingw32-cross.tgz и x86_64-w64-mingw32-native.zip. Т.к. компиляция выполняется в режиме кросс-компиляции в Linux, то соответственно выбираем архив x86_64-w64-mingw32-cross.tgz.

Заменяем текущий вариант с Linux платформой в файле Dockefile:

...
"arm64") \
	RUST_TARGET="aarch64-unknown-linux-musl" \
	MUSL="aarch64-linux-musl" \
;; \
*) \
	echo "Doesn't support $TARGETARCH architecture" \
	exit 1 \
;; \
esac \
&& wget -qO- "https://musl.cc/$MUSL-cross.tgz" | tar -xzC /root/ \
&& PATH="/root/$MUSL-cross/bin:$PATH" \
&& CC=/root/$MUSL-cross/bin/$MUSL-gcc \
&& echo "CC=$CC" \
&& rustup override set stable \
&& rustup target add "$RUST_TARGET" \
...

На вариант с Windows:

...
ARG RUST_TARGET="x86_64-pc-windows-gnu"
ARG MUSL="x86_64-w64-mingw32"

# Build
RUN wget -qO- "https://musl.cc/$MUSL-cross.tgz" | tar -xzC /root/ \
    && PATH="/root/$MUSL-cross/bin:$PATH" \
    && CC=/root/$MUSL-cross/bin/$MUSL-gcc \
    && echo "CC=$CC" \
    && rustup override set stable \
    && rustup target add "$RUST_TARGET"
...

Строка компиляции выглядит следующим образом:

RUSTFLAGS="-C linker=$CC" CC=$CC cargo build --target "$RUST_TARGET" --release --verbose --bin "sswinservice" --features "winservice"

Параметр  —verbose  выводит подробный отчет компиляции проекта.

Файл sswinservice.exe после компиляции расположится в папке  /root/shadowsocks-rust/x86_64-pc-windows-gnu/release , для удобства перенесем файл в папку  /root/shadowsocks-rust/target/release .

Теперь переходим к Docker. Тег  —output  определяет формат результата сборки образа. В документации Exporters overview определены следующие параметры:

  • image: экспортирует результат сборки в образ контейнера;
  • registry: экспортирует результат сборки в образ контейнера и отправляет его в реестр образов;
  • local: экспортирует содержимое файловой системы образа в локальный каталог;
  • tar: тоже самое, что и local, но только дополнительно упаковывается в архив tar;
  • oci: экспортирует результат сборки в локальный каталог в формате образа слоев OCI;
  • docker: экспортирует результат сборки в локальный каталог в формате спецификации Docker Image Specification v1.2.0;
  • cacheonly: не экспортирует выходные данные сборки, а только запускает сборку и кэширует результат.

Из перечня параметров необходим только вариант local. Пример вызова команды:

docker buildx build --output type=local,dest=<path/to/output> .

Где  <path/to/output>  путь сохранения результата сборки.

Итоговый вариант команды выглядит следующим образом:

docker buildx build --platform linux/amd64 -f Dockerfile.alpine.sswinservice \
    --target=artifact --output type=local,dest=out/ -t devdotnetorg/shadowsocks-rust:sswinservice .

Где  —target=artifact  определяет базовый образ, содержимое которого и получим на выходе.

В файл сборки Dockerfile.alpine.sswinservice добавим строки:

# out
FROM scratch as artifact
COPY --from=builder /root/shadowsocks-rust/target/release/sswinservice.exe /

Определяется базовый образ artifact, куда в него копируется файл sswinservice.exe.

Итоговый скрипт сборки sswinservice.exe для версии v1.18.0 (на 14.02.2024), номер версии определяется переменной окружения SS_VER, при выходе новой версии замените значение:

$ SS_VER=1.18.0
$ sudo apt-get update
$ sudo apt-get install -y curl zip
$ curl -SL --output shadowsocks-rust.zip https://github.com/shadowsocks/shadowsocks-rust/archive/refs/tags/v${SS_VER}.zip
$ unzip shadowsocks-rust.zip
$ cd shadowsocks-rust-${SS_VER}
$ curl -SL --output Dockerfile.alpine.sswinservice https://raw.githubusercontent.com/devdotnetorg/shadowsocks-rust/master/Dockerfile.alpine.sswinservice
$ curl -SL --output buildx-out.sh https://raw.githubusercontent.com/devdotnetorg/shadowsocks-rust/master/buildx-out.sh
$ chmod +x buildx-out.sh
$ ./buildx-out.sh

По итогу сборки в папке  /out  появится файл sswinservice.exe:

root@ubuntu:~/shadowsocks-rust-1.18.0# tree
.
├── Cargo.lock
├── Cargo.toml
├── Cross.toml
├── Dockerfile
├── Dockerfile.alpine.sswinservice
...
├── buildx-out.sh
...
├── out
│   └── sswinservice.exe
...

83 directories, 254 files

Сборка библиотеки Libgpiod для аппаратных платформ x86/ARM32/ARM64/RISC-V

Библиотека собирается достаточно просто т.к. все манипуляции упакованы в универсальный скрипт setup-libgpiod.sh, все что требуется, это указать необходимые параметры и забрать результат сборки. Для сохранения артефактов сборки при вызове скрипта указывается параметр  —artifact yes . После компиляции библиотеки из исходного кода, результат сборки в виде архива, например libgpiod-bin-2.1-ubuntu-23.10-aarch64.zip, будет скопирован в папку  /out/ , т.е. полный путь составит  /out/libgpiod-bin-2.1-ubuntu-23.10-aarch64.zip .

Запускаем сборку используя файл Dockerfile.ubuntu или Dockerfile.alpine, командой:

$ docker buildx build --platform linux/arm,linux/arm64,linux/amd64 \
    -f Dockerfile.ubuntu --build-arg LIB_VERSION=2.1 \
    --build-arg IMAGE_VERSION=ubuntu:22.04 --target=artifact \
    --output type=local,dest=out/ -t devdotnetorg/libgpiod:2.1-ubuntu-22.04 .

По итогу в папке  /out  появятся файлы: libgpiod-bin-2.1-ubuntu-22.04-armv7l.zip, libgpiod-bin-2.1-ubuntu-22.04-aarch64.zip, libgpiod-bin-2.1-ubuntu-22.04-x86_64.zip.

Скрипты для полной сборки:

  • buildx-out.sh — для сборки под архитектуры x86, ARM32, ARM64;
  • buildx-out-riscv64.sh — для сборки под архитектуру RISC-V.

Итоговый скрипт сборки библиотеки Libgpiod под разные платформы:

$ sudo apt-get update
$ sudo apt-get install -y git
$ git clone --branch=dev --depth=1 https://github.com/devdotnetorg/docker-libgpiod.git
$ cd docker-libgpiod
$ rm -rf .git
$ rm -rf out/*
$ chmod +x buildx-out.sh
$ chmod +x buildx-out-riscv64.sh
$ ./buildx-out.sh
$ ./buildx-out-riscv64.sh

Ресурсы

Вам также может понравиться

About the Author: Anton

Programistik