{"id":13587425,"url":"https://github.com/tonistiigi/xx","last_synced_at":"2025-04-09T05:12:40.639Z","repository":{"id":39991849,"uuid":"150307921","full_name":"tonistiigi/xx","owner":"tonistiigi","description":"Dockerfile cross-compilation helpers","archived":false,"fork":false,"pushed_at":"2024-09-19T13:54:46.000Z","size":1790,"stargazers_count":386,"open_issues_count":20,"forks_count":31,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-10-20T17:40:41.871Z","etag":null,"topics":["alpine","cross-compiler","debian","docker","dockerfile"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tonistiigi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-25T18:02:01.000Z","updated_at":"2024-10-17T23:12:54.000Z","dependencies_parsed_at":"2023-09-25T23:41:42.573Z","dependency_job_id":"a8492e1e-cab3-49de-8357-a589de226fda","html_url":"https://github.com/tonistiigi/xx","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonistiigi%2Fxx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonistiigi%2Fxx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonistiigi%2Fxx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonistiigi%2Fxx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tonistiigi","download_url":"https://codeload.github.com/tonistiigi/xx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247980844,"owners_count":21027808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alpine","cross-compiler","debian","docker","dockerfile"],"created_at":"2024-08-01T15:06:12.378Z","updated_at":"2025-04-09T05:12:40.615Z","avatar_url":"https://github.com/tonistiigi.png","language":"Shell","funding_links":[],"categories":["Shell","docker"],"sub_categories":[],"readme":"# xx - Dockerfile cross-compilation helpers\n\n[![GitHub release](https://img.shields.io/github/release/tonistiigi/xx.svg?style=flat-square)](https://github.com/tonistiigi/xx/releases/latest)\n[![CI Status](https://img.shields.io/github/actions/workflow/status/tonistiigi/xx/build.yml?label=build\u0026logo=github\u0026style=flat-square)](https://github.com/tonistiigi/xx/actions?query=workflow%3Abuild)\n[![Go Report Card](https://goreportcard.com/badge/github.com/tonistiigi/xx?style=flat-square)](https://goreportcard.com/report/github.com/tonistiigi/xx)\n[![Docker Pulls](https://img.shields.io/docker/pulls/tonistiigi/xx.svg?style=flat-square\u0026logo=docker)](https://hub.docker.com/r/tonistiigi/xx/)\n\n`xx` provides tools to support cross-compilation from Dockerfiles that understand the `--platform` flag passed in from `docker build` or `docker buildx build`. These helpers allow you to build multi-platform images from any architecture into any architecture supported by your compiler with native performance. Adding `xx` to your Dockerfile should only need minimal updates and should not require custom conditions for specific architectures.\n\n___\n\n* [Dockerfile cross-compilation primer](#dockerfile-cross-compilation-primer)\n* [Installation](#installation)\n* [Supported targets](#supported-targets)\n* [`xx-info` - Information about the build context](#xx-info---information-about-the-build-context)\n  * [Parsing current target](#parsing-current-target)\n  * [Architecture formats](#architecture-formats)\n  * [Target triple](#target-triple)\n  * [Build context](#build-context)\n* [`xx-apk`, `xx-apt`, `xx-apt-get` - Installing packages for target architecture](#xx-apk-xx-apt-xx-apt-get---installing-packages-for-target-architecture)\n* [`xx-verify` - Verifying compilation results](#xx-verify---verifying-compilation-results)\n* [C/C++](#cc)\n  * [Building on Alpine](#building-on-alpine)\n  * [Building on Debian](#building-on-debian)\n  * [Wrapping as default](#wrapping-as-default)\n* [Autotools](#autotools)\n* [CMake](#cmake)\n* [Go / Cgo](#go--cgo)\n* [Rust](#rust)\n  * [Building on Alpine](#building-on-alpine-1)\n  * [Building on Debian](#building-on-debian-1)\n* [External SDK support](#external-sdk-support)\n* [Used by](#used-by)\n* [Issues](#issues)\n\n## Dockerfile cross-compilation primer\n\nCross-compilation can be achieved in Dockerfiles by using multi-stage builds and defining some of the stages to always run on the native architecture used by the builder and execute the cross-compiling compiler. By default, a Dockerfile stage started with `FROM` keyword default to the target architecture, but this can be overridden with a `FROM --platform` flag. Using [automatic platform ARGs in global scope](https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope), the platform of the cross-compiler stage can be set to `$BUILDPLATFORM` while the value of `$TARGETPLATFORM` can be passed to the compiler with an environment variable.\n\nAfter compilation, the resulting assets can be copied into another stage that will become the result of the build. Usually, this stage does not use `FROM --platform` so that every stage is based on the expected target architecture.\n\n```dockerfile\nFROM --platform=$BUILDPLATFORM alpine AS xbuild\nARG TARGETPLATFORM\nRUN ./compile --target=$TARGETPLATFORM -o /out/myapp\n\nFROM alpine\nCOPY --from=xbuild /out/myapp /bin\n```\n\n## Installation\n\n`xx` is distributed with a Docker image `tonistiigi/xx` that contains a collection of helper scripts that read `TARGET*` environment variables to automatically configure the compilation targets. The scripts are based on Posix Shell, so they should work on top of any image but currently `xx` is expected to work on Alpine and Debian/Ubuntu based distros. In order to avoid unexpected changes, you may want to pin the image using an immutable digest. Although `xx` only contains shell scripts that are identical for every platform it is recommended to also import `xx` with `FROM --platform=$BUILDPLATFORM`, so that import commands are shared for all compilation targets.\n\n```dockerfile\nFROM --platform=$BUILDPLATFORM tonistiigi/xx AS xx\n\nFROM --platform=$BUILDPLATFORM alpine\n# copy xx scripts to your build stage\nCOPY --from=xx / /\n# export TARGETPLATFORM (or other TARGET*)\nARG TARGETPLATFORM\n# you can now call xx-* commands\nRUN xx-info env\n```\n\n`xx` currently contains `xx-info`, `xx-apk`, `xx-apt-get`, `xx-cc`, `xx-c++`, `xx-clang`, `xx-clang++`, `xx-go`, `xx-cargo`, `xx-verify`. `xx-clang` (and its aliases) creates additional aliases, eg. `${triple}-clang`, `${triple}-pkg-config`, on first invocation or on `xx-clang --setup-target-triple` call.\n\n## Supported targets\n\n`xx` supports building from and into Linux amd64, arm64, arm/v7, s390x, ppc64le and 386, and Alpine, Debian and Ubuntu. Risc-V is supported for Go and Rust builds and for newer distros that provide Risc-V packages like `alpine:edge` or `debian:sid`.\n\nGo builds that don't depend on system packages can additionally target MacOS and Windows on all architectures. C/C++/CGo/Rust builds are supported for MacOS targets when an external SDK image is provided.\n\n`xx-info` command also works on RHEL-style distros but no support is provided for package manager wrappers(eg. yum, dnf) there.\n\n## `xx-info` - Information about the build context\n\n`xx-info` command returns normalized information about the current build context. It allows you to get various information about your build target and configuration and avoid the need for converting from one format to another in your own code. Invoking `xx-info` without any additional arguments will invoke `xx-info triple`.\n\n### Parsing current target\n\n- `xx-info os` - prints operating system component of TARGETPLATFORM (linux,darwin,windows,wasi)\n- `xx-info arch` - architecture component of TARGETPLATFORM\n- `xx-info variant`  - variant component of TARGETPLATFORM if architecture is arm (eg. v7\n\n### Architecture formats\n\nThese commands return architecture names as used by specific tools to avoid conversion and tracking exceptions in your own code. E.g. arm64 repositories are called `aarch64` in Alpine, but `arm64` in Debian. `uname -m` returns `aarch64` in Linux, but `arm64` in Darwin etc.\n\n- `xx-info march` - Target machine architecture that is expected to match value of `uname -m`\n- `xx-info alpine-arch`  - Target architecture for [Alpine package repositories](https://pkgs.alpinelinux.org/packages)\n- `xx-info debian-arch` - Target architecture for [Debian package repositories](https://www.debian.org/ports/)\n- `xx-info rhel-arch` - Target architecture for [RPM package repositories](https://docs.fedoraproject.org/ro/Fedora_Draft_Documentation/0.1/html/RPM_Guide/ch01s03.html)\n- `xx-info pkg-arch` - Either alpine-arch, debian-arch or rhel-arch depending on the context\n\n### Target triple\n\nTarget triple is the target format taken as input in various gcc and llvm based compilers.\n\n- `xx-info triple` - Target triple in arch[-vendor]-os-abi form. This command is the default.\n- `xx-info vendor` - Vendor component of target triple\n- `xx-info libc` - Used libc (musl or gnu)\n\n### Build context\n\n- `xx-info is-cross` - Exit cleanly if target is not native architecture\n- `xx-info env` - Print XX_* variables defining target environment\n\n```console\n$ xx-info env\nXX_OS=linux\nXX_MARCH=x86_64\nXX_VENDOR=alpine\nXX_PKG_ARCH=x86_64\nXX_TRIPLE=x86_64-alpine-linux-musl\nXX_LIBC=musl\nTARGETOS=linux\nTARGETARCH=amd64\nTARGETVARIANT=\n```\n\n## `xx-apk`, `xx-apt`, `xx-apt-get` - Installing packages for target architecture\n\nThese scripts allow managing packages (most commonly installing new packages) from either Alpine or Debian repositories. They can be invoked with any arguments regular `apk` or `apt/apt-get` commands accept. If cross-compiling for non-native architectures, the repositories for the target architecture are added automatically, and packages are installed from there. On Alpine, installing packages for a different architecture under the same root is not allowed, so `xx-apk` installs packages under a secondary root `/${triple}`. These scripts are meant for installing headers and libraries that compilers may need. To avoid unnecessary garbage, the non-native binaries under `*/bin` are skipped on installation.\n\n```dockerfile\n# alpine\nARG TARGETPLATFORM\nRUN xx-apk add --no-cache musl-dev zlib-dev\n```\n\n```dockerfile\n# debian\nARG TARGETPLATFORM\nRUN xx-apt-get install -y libc6-dev zlib1g-dev\n```\n\n\u003e [!NOTE]\n\u003e `xx-apt --print-source-file` can be used to print the path of the main [Apt sources configuration file](https://manpages.debian.org/bookworm/apt/sources.list.5.en.html)\n\nInstalling two meta-libraries, `xx-c-essentials`, `xx-cxx-essentials` is also allowed that expand the minimum necessary packages for either base image.\n\n## `xx-verify` - Verifying compilation results\n\n`xx-verify` allows verifying that the cross-compile toolchain was correctly configured and outputted binaries for the expected target platform. `xx-verify` works by calling `file` utility and comparing the expected output. Optionally `--static` option can be passed to verify that the compiler produced a static binary that can be safely copied to another Dockerfile stage without runtime libraries. If the binary does not match the expected value, `xx-verify` returns with a non-zero exit code and error message.\n\n```dockerfile\nARG TARGETPLATFORM\nRUN xx-clang --static -o /out/myapp app.c \u0026\u0026 \\\n    xx-verify --static /out/myapp\n```\n\n\u003e [!NOTE]\n\u003e `XX_VERIFY_STATIC=1` environment variable can be defined to make `xx-verify`\n\u003e always verify that the compiler produced a static binary.\n\n## C/C++\n\nThe recommended method for C-based build is to use `clang` via `xx-clang` wrapper. Clang is natively a cross-compiler, but in order to use it, you also need a linker, compiler-rt or libgcc, and a C library(musl or glibc). All these are available as packages in Alpine and Debian based distros. Clang and linker are binaries and should be installed for your build architecture, while libgcc and C library should be installed for your target architecture.\n\nThe recommended linker is `lld`, but there are some caveats. `lld` is not supported on S390x, and based on our experience, sometimes has issues with preparing static binaries for Ppc64le. In these cases, `ld` from `binutils` is required. As separate `ld` binary needs to be built for each architecture, distros often do not provide it as a package. Therefore `xx` loads [prebuilt](https://github.com/tonistiigi/xx/releases/tag/prebuilt%2Fld-1) `ld` binaries when needed. `XX_CC_PREFER_LINKER=ld` can be defined if you want to always use `ld`, even when `lld` is available on the system. Building MacOS binaries happens through a prebuilt `ld64` linker that also adds ad-hoc code-signature to the resulting binary.\n\n`xx-clang` can be called with any arguments `clang` binary accepts and will internally call the native `clang` binary with additional configuration for correct cross-compilation. On first invocation, `xx-clang` will also set up alias commands for the current target triple that can be later called directly. This helps with tooling that looks for programs with a target triple prefix from your `PATH`. This setup phase can be manually invoked by calling `xx-clang --setup-target-triple` that is a special flag that `clang` itself does not implement.\n\nAlias commands include:\n\n- `triple-clang`, `triple-clang++` if `clang` is installed\n- `triple-ld` if `ld` is used as linker\n- `triple-pkg-config` if `pkg-config` is installed\n- `triple-addr2line`, `triple-ar`, `triple-as`, `triple-ranlib`, `triple-nm`, `triple-dlltool`, `triple-strip` if cross-compilation capable tools are available though `llvm` package\n- `triple-windres` if `llvm-rc` is installed and compiling for Windows\n\nAlias commands can be called directly and always build the configuration specified by their name, even if `TARGETPLATFORM` value has changed.\n\n### Building on Alpine\n\nOn Alpine, there is no special package for `libgcc` so you need to install `gcc` package with `xx-apk` even though the build happens through clang. To use compiler-rt instead of `libgcc` `--rtlib` needs to be passed manually. We will probably add default detection/loading for compiler-rt in the future to simplify this part. Default libc used in Alpine is [Musl](https://www.musl-libc.org/) that can be installed with `musl-dev` package.\n\n```dockerfile\n# ...\nRUN apk add clang lld\n# copy source\nARG TARGETPLATFORM\nRUN xx-apk add gcc musl-dev\nRUN xx-clang -o hello hello.c \u0026\u0026 \\\n    xx-verify hello\n```\n\nClang binary can also be called directly with `--target` flag if you want to avoid `xx-` prefixes. `--print-target-triple` is a built-in flag in clang that can be used to query to correct default value.\n\n```dockerfile\n# ...\nRUN xx-apk add g++\nRUN clang++ --target=$(xx-clang --print-target-triple) -o hello hello.cc\n```\n\nOn the first invocation, aliases with `triple-` prefix are set up so the following also works:\n\n```dockerfile\n# ...\nRUN $(xx-clang --print-target-triple)-clang -o hello hello.c\n```\n\nIf you prefer aliases to be created as a separate step on a separate layer, you can use `--setup-target-triple`.\n\n```dockerfile\n# ...\nRUN xx-clang --setup-target-triple\nRUN $(xx-info)-clang -o hello hello.c\n```\n\n### Building on Debian\n\nBuilding on Debian/Ubuntu is very similar. The only required dependency that needs to be installed with `xx-apt` is `libc6-dev` or `libstdc++-N-dev` for C++.\n\n```dockerfile\n# ...\nRUN apt-get update \u0026\u0026 apt-get install -y clang lld\n# copy source\nARG TARGETPLATFORM\nRUN xx-apt install -y libc6-dev\nRUN xx-clang -o hello hello.c\n```\n\nRefer to the previous section for other variants.\n\nIf you wish to build with GCC instead of Clang you need to install `gcc` and `binutils` packages additionally with `xx-apt-get`. `xx-apt-get` will automatically install the packages that generate binaries for the current target architecture. You can then call GCC directly with the correct target triple. Note that Debian currently only provides GCC cross-compilation packages if your native platform is amd64 or arm64.\n\n```dockerfile\n# ...\n# copy source\nARG TARGETPLATFORM\nRUN xx-apt-get install -y binutils gcc libc6-dev\nRUN $(xx-info)-gcc -o hello hello.c\n```\n\n### Wrapping as default\n\nSpecial flags `xx-clang --wrap` and `xx-clang --unwrap` can be used to override the default behavior of `clang` with `xx-clang` in the extreme cases where your build scripts have no way to point to alternative compiler names.\n\n```\n# export TARGETPLATFORM=linux/amd64\n# xx-clang --print-target-triple\nx86_64-alpine-linux-musl\n# clang --print-target-triple\nx86_64-alpine-linux-musl\n# \n# xx-clang --wrap\n# clang --print-target-triple\nx86_64-alpine-linux-musl\n# xx-clang --unwrap\n# clang --print-target-triple\naarch64-alpine-linux-musl\n```\n\n## Autotools\n\nAutotools has [built-in support](https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html) for cross-compilation that works by passing `--host`, `--build`, and `--target` flags to the configure script. `--host` defines the target architecture of the build result, `--build` defines compilers native architecture(used for compiling helper tools etc.), and `--target` defines an architecture that the binary returns if it is running as a compiler of other binaries. Usually, only `--host` is needed.\n\n```dockerfile\n# ...\nARG TARGETPLATFORM\nRUN ./configure --host=$(xx-clang --print-target-triple) \u0026\u0026 make\n```\n\nIf you need to pass `--build`, you can temporarily reset the `TARGETPLATFORM` variable to get the system value.\n\n```dockerfile\nARG TARGETPLATFORM\nRUN ./configure --host=$(xx-clang --print-target-triple) --build=$(TARGETPLATFORM= xx-clang --print-target-triple) \u0026\u0026 make\n```\n\nSometimes `configure` scripts misbehave and don't work correctly unless the name of the C compiler is passed directly. In these cases, you can use overrides like:\n\n```dockerfile\nRUN CC=xx-clang ./configure ...\n```\n\n```dockerfile\nRUN ./configure --with-cc=xx-clang ...\n```\n\n```dockerfile\nRUN ./configure --with-cc=$(xx-clang --print-target-triple)-clang ...\n```\n\n## CMake\n\nIn order to make cross-compiling with CMake easier, `xx-clang` has a special flag `xx-clang --print-cmake-defines`. Running that command returns the following Cmake definitions:\n\n```\n-DCMAKE_C_COMPILER=clang\n-DCMAKE_CXX_COMPILER=clang++\n-DCMAKE_ASM_COMPILER=clang\n-DPKG_CONFIG_EXECUTABLE=\"$(xx-clang --print-prog-name=pkg-config)\"\n-DCMAKE_C_COMPILER_TARGET=\"$(xx-clang --print-target-triple)\"\n-DCMAKE_CXX_COMPILER_TARGET=\"$(xx-clang++ --print-target-triple)\"\n-DCMAKE_ASM_COMPILER_TARGET=\"$(xx-clang --print-target-triple)\"\n```\n\nUsually, this should be enough to pick up the correct configuration.\n\n```dockerfile\nRUN apk add cmake clang lld\nARG TARGETPLATFORM\nRUN xx-apk musl-dev gcc\nRUN mkdir build \u0026\u0026 cd build \u0026\u0026 \\\n    cmake $(xx-clang --print-cmake-defines) ..\n```\n\n## Go / Cgo\n\nBuilding Go can be achieved with the `xx-go` wrapper that automatically sets up values for `GOOS`, `GOARCH`, `GOARM`, `GOAMD64` etc. It also sets up `pkg-config` and C compiler if building with CGo. Note that by default, CGo is enabled in Go when compiling for native architecture and disabled when cross-compiling. This can easily produce unexpected results; therefore, you should always define either `CGO_ENABLED=1` or `CGO_ENABLED=0` depending on if you expect your compilation to use CGo or not.\n\n```dockerfile\nFROM --platform=$BUILDPLATFORM golang:alpine\n# ...\nARG TARGETPLATFORM\nENV CGO_ENABLED=0\nRUN xx-go build -o hello ./hello.go \u0026\u0026 \\\n    xx-verify hello\n```\n\n```dockerfile\nFROM --platform=$BUILDPLATFORM golang:alpine\nRUN apk add clang lld\n# ...\nARG TARGETPLATFORM\nRUN xx-apk add musl-dev gcc\nENV CGO_ENABLED=1\nRUN xx-go build -o hello ./hello.go \u0026\u0026 \\\n    xx-verify hello\n```\n\nIf you want to make `go` compiler cross-compile by default, you can use `xx-go --wrap` and `xx-go --unwrap`\n\n```dockerfile\n# ...\nRUN xx-go --wrap\nRUN go build -o hello hello.go \u0026\u0026 \\\n    xx-verify hello\n```\n\n## Rust\n\nBuilding Rust can be achieved with the `xx-cargo` wrapper that automatically\nsets up the target triple and also `pkg-config` and C compiler.\n\nThe wrapper supports rust installed via [`rustup`](https://rustup.rs/)\n(alpine/debian), distribution packages (alpine/debian) and the [official `rust` image](https://hub.docker.com/_/rust).\n\n### Building on Alpine\n\n```dockerfile\n# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM rust:alpine\nRUN apk add clang lld\n# ...\nARG TARGETPLATFORM\nRUN xx-cargo build --release --target-dir ./build \u0026\u0026 \\\n    xx-verify ./build/$(xx-cargo --print-target-triple)/release/hello_cargo\n```\n\nCargo binary can also be called directly with `--target` flag if you don't want\nto use the wrapper. `--print-target-triple` is a built-in flag that can be used\nto set the correct target:\n\n```dockerfile\n# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM rust:alpine\nRUN apk add clang lld\n# ...\nARG TARGETPLATFORM\nRUN cargo build --target=$(xx-cargo --print-target-triple) --release --target-dir ./build \u0026\u0026 \\\n    xx-verify ./build/$(xx-cargo --print-target-triple)/release/hello_cargo\n```\n\n\u003e [!NOTE]\n\u003e `xx-cargo --print-target-triple` does not always have the same value as\n\u003e `xx-clang --print-target-triple`. This is because prebuilt Rust and C\n\u003e libraries sometimes use a different value.\n\nThe first invocation of `xx-cargo` will install the standard library for Rust\nmatching the target if not already installed.\n\nTo fetch dependencies from crates.io you can use `cargo fetch` before building:\n\n```dockerfile\n# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM rust:alpine\nRUN apk add clang lld\n# ...\nRUN --mount=type=cache,target=/root/.cargo/git/db \\\n    --mount=type=cache,target=/root/.cargo/registry/cache \\\n    --mount=type=cache,target=/root/.cargo/registry/index \\\n    cargo fetch\nARG TARGETPLATFORM\nRUN --mount=type=cache,target=/root/.cargo/git/db \\\n    --mount=type=cache,target=/root/.cargo/registry/cache \\\n    --mount=type=cache,target=/root/.cargo/registry/index \\\n    xx-cargo build --release --target-dir ./build \u0026\u0026 \\\n    xx-verify ./build/$(xx-cargo --print-target-triple)/release/hello_cargo\n```\n\n\u003e [!NOTE]\n\u003e By calling `cargo fetch` before `ARG TARGETPLATFORM` your packages are\n\u003e fetched only once for the whole build while the building happens separately\n\u003e for each target architecture.\n\nTo avoid redownloading dependencies on every build, you can use cache mounts\nto store [Git sources with packages and metadata of crate registries](https://doc.rust-lang.org/cargo/guide/cargo-home.html#directories).\n\nIf you don't want to use the official Rust image, you can install `rustup`\nmanually:\n\n```dockerfile\n# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM alpine AS rustup\nRUN apk add curl\nRUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable --no-modify-path --profile minimal\nENV PATH=\"/root/.cargo/bin:$PATH\"\n\nFROM rustup\nRUN apk add clang lld\n# ...\nARG TARGETPLATFORM\nRUN xx-cargo build --release --target-dir ./build \u0026\u0026 \\\n    xx-verify ./build/$(xx-cargo --print-target-triple)/release/hello_cargo\n```\n\nIf you install rust using distribution packages, `rustup` will not be available:\n\n```dockerfile\n# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM alpine\nRUN apk add clang lld rust cargo\n# ...\nARG TARGETPLATFORM\nRUN xx-apk add xx-c-essentials\nRUN xx-cargo build --release --target-dir ./build \u0026\u0026 \\\n    xx-verify ./build/$(xx-cargo --print-target-triple)/release/hello_cargo\n```\n\nIn this case, you need to also install minimum necessary packages using `xx-apk`.\n\n### Building on Debian\n\nBuilding on Debian/Ubuntu is very similar. If you are using `rustup`:\n\n```dockerfile\n# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM rust:bookworm\nRUN apt-get update \u0026\u0026 apt-get install -y clang lld\nARG TARGETPLATFORM\nRUN xx-cargo build --release --target-dir ./build \u0026\u0026 \\\n    xx-verify ./build/$(xx-cargo --print-target-triple)/release/hello_cargo\n```\n\n```dockerfile\n# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM debian:bookworm AS rustup\nRUN apt-get update \u0026\u0026 apt-get install -y curl ca-certificates\nRUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable --no-modify-path --profile minimal\nENV PATH=\"/root/.cargo/bin:$PATH\"\n\nFROM rustup\nRUN apt-get update \u0026\u0026 apt-get install -y clang lld\n# ...\nARG TARGETPLATFORM\nRUN xx-cargo build --release --target-dir ./build \u0026\u0026 \\\n    xx-verify ./build/$(xx-cargo --print-target-triple)/release/hello_cargo\n```\n\nOr distribution packages:\n\n```dockerfile\n# syntax=docker/dockerfile:1\nFROM --platform=$BUILDPLATFORM debian:bookworm\nRUN apt-get update \u0026\u0026 apt-get install -y clang lld cargo\nARG TARGETPLATFORM\nRUN xx-apt-get install xx-c-essentials\nRUN xx-cargo build --release --target-dir ./build \u0026\u0026 \\\n    xx-verify ./build/$(xx-cargo --print-target-triple)/release/hello_cargo\n```\n\n## External SDK support\n\nIn addition to Linux targets, `xx` can also build binaries for MacOS and Windows. When building MacOS binaries from C, external MacOS SDK is needed in `/xx-sdk` directory. Such SDK can be built, for example, with [gen_sdk_package script in osxcross project](https://github.com/tpoechtrager/osxcross/blob/master/tools/gen_sdk_package.sh). Please consult XCode license terms when making such an image. `RUN --mount` syntax can be used in Dockerfile in order to avoid copying SDK files. No special tooling such as `ld64` linker is required in the image itself.\n\nBuilding Windows binaries from C/CGo is currently a work in progress and not functional.\n\n```dockerfile\n# syntax=docker/dockerfile:1.2\n# ...\nRUN apk add clang lld\nARG TARGETPLATFORM\nRUN --mount=from=my/sdk-image,target=/xx-sdk,src=/xx-sdk \\\n    xx-clang -o /hello hello.c \u0026\u0026 \\\n    xx-verify /hello\n\nFROM scratch\nCOPY --from=build /hello /\n```\n\n```console\ndocker buildx build --platform=darwin/amd64,darwin/arm64 -o bin .\n```\n\n`-o/--output` flag can be used to export binaries out from the builder without creating a container image.\n\n## Used by\n\nThese projects, as well as [xx Dockerfile](https://github.com/tonistiigi/xx/blob/41f7f39551857836e691da81580296ba5acf6ac3/base/Dockerfile) can be used for reference.\n\n- [BuildKit](https://github.com/moby/buildkit/blob/8d5c5f197489f76e2663c417a9e71d42464fa3cd/Dockerfile)\n- [Docker CLI](https://github.com/docker/cli/blob/86e1f04b5f115fb0b4bbd51e0e4a68233072d24b/Dockerfile)\n- [Binfmt (Qemu)](https://github.com/tonistiigi/xx/blob/8703596e93946b9e31161c060a9ac41a8b578c3f/Dockerfile)\n- [Docker Buildx](https://github.com/docker/buildx/blob/4fec647b9d8f34f8569141124d8462c912858144/Dockerfile)\n- [Containerd](https://github.com/containerd/containerd/blob/9e7910ebdcbf3bf10ebd0a282ab9996572e38749/.github/workflows/release/Dockerfile)\n\n## Issues\n\n`xx` project welcomes contributions if you notice any issues or want to extend the capabilities with new features. We are also interested in cases where a popular project does not compile easily with `xx` so it can be improved, and tests can be added that try building these projects when `xx` gets updated. If you want to add support for a new architecture or language, please open an issue first to verify that the proposal matches the scope or `xx`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftonistiigi%2Fxx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftonistiigi%2Fxx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftonistiigi%2Fxx/lists"}