Кросс-компиляция позволяет получать исполняемый код для платформы, отличной от той, на которой запускается этот процесс. В данном случае рассмотрим кросс-компиляцию в 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