9.3 KiB
Contributing
Getting started
How to add a new image ? (With crosstool-ng)
In this part, we will see how to add a new image, we will take example with linux-arm64 for a raspberry pi 4, with crosstool-ng.
Build and config crosstool-ng
To start, you need to download the source code of crosstool-ng:
git clone --recurse-submodules --remote-submodules https://github.com/crosstool-ng/crosstool-ng.git
Go to crosstool-ng folder:
cd crosstool-ng
Change git branch:
git checkout crosstool-ng-1.27.0
Once in the crosstool-ng folder, you must first run the bootstrap script:
./bootstrap
Then run the configure script:
Note: -enable-local does a portable install of crosstool-ng.:
./configure --enable-local
Finally, launch the building of crosstool-ng:
make -j$(nproc)
Once the crosstool-ng build is complete, you can run this command to test crosstool-ng:
./ct-ng --version
Before starting the configuration of the toolchains, i recommend you to use one of the examples from crosstool-ng and then make your changes, the command to display the examples:
./ct-ng list-samples
We will take the example of aarch64-rpi4-linux-gnu, a .config file will be created:
./ct-ng aarch64-rpi4-linux-gnu
Alternatively, we could copy an existing crosstool-ng.config from one of the target folders in the dockcross project to the local .config:
cp path/to/dockcross/linux-arm64/crosstool-ng.config .config
We will configure the toolchains according to our needs:
./ct-ng menuconfig
Once the modifications are made, we will display the name of the toolchains, it will be useful later:
./ct-ng show-tuple
Configuring docker image
You must create a file with the same name of the docker image (linux-arm64).
Copy the .config of crosstool-ng to this file (linux-arm64) and rename it to crosstool-ng.config.
You need to create a file named Toolchain.cmake in linux-arm64.
Copy text to Toolchain.cmake file:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR ARM64)
set(cross_triple $ENV{CROSS_TRIPLE})
set(cross_root $ENV{CROSS_ROOT})
set(CMAKE_C_COMPILER $ENV{CC})
set(CMAKE_CXX_COMPILER $ENV{CXX})
set(CMAKE_Fortran_COMPILER $ENV{FC})
set(CMAKE_CXX_FLAGS "-I ${cross_root}/include/")
list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH} ${cross_root} ${cross_root}/${cross_triple})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_SYSROOT ${cross_root}/${cross_triple}/sysroot)
set(CMAKE_CROSSCOMPILING_EMULATOR /usr/bin/qemu-arm64)
Then you must change these lines according to the targeted architecture, here ARM64:
set(CMAKE_SYSTEM_PROCESSOR ARM64)
set(CMAKE_CROSSCOMPILING_EMULATOR /usr/bin/qemu-arm64)
Then you must create a file named Dockerfile.in in the image folder (linux-arm64).
Copy text to Dockerfile.in file:
ARG ORG=dockcross
FROM ${ORG}/base:latest
LABEL maintainer="Matt McCormick matt@mmmccormick.com"
# This is for 64-bit ARM Linux machine
# Crosstool-ng crosstool-ng-1.25.0 2022-05-13
ENV CT_VERSION=crosstool-ng-1.25.0
#include "common.crosstool"
# The cross-compiling emulator
RUN apt-get update \
&& apt-get install -y \
qemu-user \
qemu-user-static \
&& apt-get clean --yes
# The CROSS_TRIPLE is a configured alias of the "aarch64-unknown-linux-gnu" target.
ENV CROSS_TRIPLE=aarch64-unknown-linux-gnu
ENV CROSS_ROOT=${XCC_PREFIX}/${CROSS_TRIPLE}
ENV AS=${CROSS_ROOT}/bin/${CROSS_TRIPLE}-as \
AR=${CROSS_ROOT}/bin/${CROSS_TRIPLE}-ar \
CC=${CROSS_ROOT}/bin/${CROSS_TRIPLE}-gcc \
CPP=${CROSS_ROOT}/bin/${CROSS_TRIPLE}-cpp \
CXX=${CROSS_ROOT}/bin/${CROSS_TRIPLE}-g++ \
LD=${CROSS_ROOT}/bin/${CROSS_TRIPLE}-ld \
FC=${CROSS_ROOT}/bin/${CROSS_TRIPLE}-gfortran
ENV QEMU_LD_PREFIX="${CROSS_ROOT}/${CROSS_TRIPLE}/sysroot"
ENV QEMU_SET_ENV="LD_LIBRARY_PATH=${CROSS_ROOT}/lib:${QEMU_LD_PREFIX}"
COPY Toolchain.cmake ${CROSS_ROOT}/
ENV CMAKE_TOOLCHAIN_FILE=${CROSS_ROOT}/Toolchain.cmake
ENV PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
# Linux kernel cross compilation variables
ENV PATH=${PATH}:${CROSS_ROOT}/bin
ENV CROSS_COMPILE=${CROSS_TRIPLE}-
ENV ARCH=arm64
#include "common.label-and-env"
Then you must change these lines according to the targeted architecture.
Here you have to change the value according to the name of the toolchain (./ct-ng show-tuple):
ENV CROSS_TRIPLE=aarch64-unknown-linux-gnu
These lines also need to be changed:
LABEL maintainer="Matt McCormick matt@mmmccormick.com"
ENV PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
ENV ARCH=arm64
Once this part is finished, there must be 3 files in the linux-arm64 folder:
crosstool-ng.config, the configuration of the toolchain/crosstool-ng.Dockerfile.in, the docker file.Toolchain.cmake, the CMake file for the toolchains.
Makefile
The Makefile is generic: it knows how to generate/build/test/push
an image out of a "preset" directory, but it has no built-in knowledge of the
actual list of dockcross images. Every image folder is a preset that is
auto-discovered because it contains a preset.mk file - there is no central
list to edit anymore.
To add the image to the build system, create linux-arm64/preset.mk:
BASE_IMAGE_REGISTRY ?= docker.io
BASE_IMAGE_PATH ?= dockcross
BASE_IMAGE_NAME = base
BASE_IMAGE_TAG = latest
OUTPUT_IMAGE_REGISTRY ?= docker.io
OUTPUT_IMAGE_PATH ?= dockcross
OUTPUT_IMAGE_NAME = linux-arm64
BASE_IMAGE_NAME = base / BASE_IMAGE_TAG = latest matches the common case
where Dockerfile.in starts with ARG ORG=dockcross / FROM ${ORG}/base:latest
(the image is built on top of the local base preset). OUTPUT_IMAGE_NAME
should match the folder name so the built image is tagged
<registry>/<path>/linux-arm64.
A preset.mk can override or add anything the generic Makefile doesn't
already do the right thing for, for example:
TEST_IMAGE_CMD/TEST_IMAGE_ARGS: override the command run bymake <image>.test(defaults topython3 test/run.py). E.g. an image that only supports C needsTEST_IMAGE_ARGS = --languages C.BUILD_CONTEXT_DIR: override the docker build context directory. Defaults to the preset's own folder; a few images (the manylinux*-aarch64/x64/x86 family)COPYfiles from sibling folders and needBUILD_CONTEXT_DIR = $(PRESET_DIR)(the repository root) instead.DEPENDS_ON: list of<preset>.buildtargets that must be built first, for images whoseDockerfile.inisFROM ${ORG}/<other-image>:latest(e.g.manylinux2014-aarch64depends onmanylinux2014-x64).EXTRA_BUILD_ARGS: extra--build-arg KEY=VALUEflags.STATIC_DOCKERFILE = yes: for the rare image that ships a committedDockerfileinstead of a generatedDockerfile.in.
See linux-arm64/preset.mk and, for the more involved cases, manylinux2014-aarch64/preset.mk or web-wasm/preset.mk.
Image building and testing
You can now start building the image:
make linux-arm64
When finished, you can test it:
make linux-arm64.test
If you want to go a little further in the tests:
docker run --rm linux-arm64 > ./linux-arm64
chmod +x ./linux-arm64
And then run the commands to build a project (you must be in the directory of your project to build):
./linux-arm64 make
With CMake + Ninja:
./linux-arm64 cmake -Bbuild -S. -GNinja
./linux-arm64 ninja -Cbuild
CI (github action)
To finish, you have to add the image/folder name to the images job's matrix
in .github/workflows/main.yml:
# Linux arm64/armv8 images
- { image: "linux-arm64", multiarch: "yes" }
multiarch: "yes" is only for the handful of images that are natively built
and published for both amd64 and arm64 (see deploy-multi-arch-images in the
same file); everything else should leave it empty.
That's the only thing the CI workflow needs to know. It builds the image with
make <image>, runs the fast in-container smoke test with make <image>.test, and then make <image>.test-extra - which builds a handful of
real upstream projects (Stockfish, CMake, OpenSSL, CPython, ...) with the
image's toolchain. Which of those extra tests apply to this image and their
architecture-specific flags are not set in the workflow: they belong in
the image's own preset.mk, e.g.:
EXTRA_TESTS = stockfish ninja openssl SQLite fmt cpython
STOCKFISH_ARGS = ARCH=armv8
OPENSSL_ARGS = linux-aarch64
CPYTHON_ARGS = --host=aarch64-unknown-linux-gnu --target=aarch64-unknown-linux-gnu
What each extra test actually clones and builds is defined once in the Makefile ("Extra tests" section) so it stays identical across every image. You can run any of them locally, the same way CI does:
make linux-arm64.test-extra # everything EXTRA_TESTS lists
make linux-arm64.test-stockfish # one test directly, regardless of EXTRA_TESTS
Leave EXTRA_TESTS unset for images where a given upstream project doesn't
build (e.g. OpenSSL on some RISC-V targets).