Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
icedragon is a tool for easy cross (but also native) builds using LLVM
(clang, Rust).

It consists of:

- Container images which provide the toolchains for each target.
- A CLI tool, which is a wrapper about a container engine (Docker,
  podman) and allows easy execution of the toolchain.
  • Loading branch information
vadorovsky committed Feb 1, 2025
1 parent a6cf419 commit b93068d
Show file tree
Hide file tree
Showing 18 changed files with 864 additions and 3 deletions.
80 changes: 80 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: CI

on:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
- cron: 00 4 * * *

env:
CARGO_TERM_COLOR: always

jobs:
lint-stable:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: clippy, rust-src

- name: Run clippy
run: cargo clippy --all-targets --workspace -- --deny warnings

lint-nightly:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: rustfmt, rust-src

- name: Check formatting
run: cargo fmt --all -- --check

build-container-image:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-unknown-linux-musl
type: cross
- target: x86_64-unknown-linux-musl
type: native
name: container ${{ matrix.type }} ${{ matrix.target }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable

- name: Log in to GitHub Container Registry
run: |
set -euxo pipefail
echo "${{ secrets.GITHUB_TOKEN }}" | \
docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build container image
if: github.ref == 'refs/heads/main'
run: |
cargo run build-container-image \
--tag ghcr.io/${{ github.repository }}/${{ matrix.type }}-${{ matrix.target }}:latest \
--target ${{ matrix.target }} \
--push
- name: Build container image
if: github.ref != 'refs/heads/main'
run: |
cargo run build-container-image \
--tag ghcr.io/${{ github.repository }}/${{ matrix.type }}-${{ matrix.target }}:${{ github.head_ref }} \
--target ${{ matrix.target }} \
--push
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ Cargo.lock
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
#.idea/

# Added by cargo

/target
18 changes: 18 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "icedragon"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0.89"
chrono = "0.4"
clap = { version = "4.5", features = ["derive"] }
env_logger = "0.11"
log = "0.4"
target-lexicon = "0.12"
thiserror = { version = "1.0.64" }
uuid = { version = "1.10", features = ["v4"] }
which = "6.0"

[[bin]]
name = "icedragon"
99 changes: 97 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,97 @@
# cross-llvm
Containerized (cross) LLVM toolchains
# icedragon

Linux cross-compilation suite for building portable, statically linked
software.

Currently supports the following languages:

* C
* C++
* Rust

Icedragon comes in two forms:

* Container images, with a "zero-setup" set of toolchains and essential
libraries.
* CLI, which leverages the container images, but feels like using a regular
compiler on your host.

It's based on:

* [Gentoo Linux][gentoo] ([musl-llvm][gentoo-musl-llvm] flavor), which is used
as the base system for the containers.
* [crossdev][crossdev], which manages Gentoo sysroots for different
architectures.
* [musl][musl] libc, which, unlike [glibc][glibc], can be statically linked
without imposing any runtime dependencies.
* [LLVM][llvm] compiler infrastructer.
* [Rustup][rustup], which is used for managing [Rust][rust] toolchains.

## How is icedragon different from Alpine Linux?

Let's start with similarities. Both icedragon and [Alpine][alpine] are using
[musl][musl] as the C standard library. Both can be used to build portable,
statically linked binaries.

The first difference is that [Alpine][alpine] uses [GCC][gcc]
and [GNU C++ library][libstdc++]. Icedragon uses [LLVM][llvm] and
[LLVM C++ library][libcxx] and doesn't come with [GCC][gcc].

The second difference is strong focus on cross-compilation in icedragon.
Ability to build for different targets without manual setup of a cross
sysroot is one of the most important goals.

## Featured libraries

Icedragon comes with a set of static libraries which can be considered "build
essentials" for the most of C/C++ software on Linux, as well as for Rust
crates, which don't vendor C dependencies and expect them to be present in the
system.

* Compression libraries:
* brotli
* zstd
* zlib
* Cryptography libraries:
* gpgme
* OpenSSL
* JSON
* json-c
* Key-value stores
* rocksdb
* Linux-specific libraries and utilities:
* util-linux
* Network libraries:
* c-ares
* libcurl
* Regular expressions:
* libpcre2

## GNU extensions

Icedragon doesn't use glibc, but it also doesn't require rigurous POSIX
compatibility. It makes use of various projects, which provide ports of GNU
extensions, while still being compatible with a musl/LLVM toolchain:

* [argp-standalone][argp-standalone]
* [error-standalone][error-standalone]
* [llvm-libgcc][llvm-libgcc]
* [musl-fts][musl-tfs]

At the same time, these ports can be linked statically and don't issue any
`dlopen` calls.

[gentoo]: https://www.gentoo.org
[crossdev]: https://wiki.gentoo.org/wiki/Crossdev
[musl]: https://musl.libc.org
[glibc]: https://www.gnu.org/software/libc
[llvm]: https://llvm.org
[rustup]: https://rustup.rs
[rust]: https://www.rust-lang.org
[alpine]: https://www.alpinelinux.org
[gcc]: https://gcc.gnu.org
[libstdc++]: https://gcc.gnu.org/onlinedocs/libstdc++
[libcxx]: https://libcxx.llvm.org
[argp-standalone]: https://github.com/ericonr/argp-standalone
[error-standalone]: https://hacktivis.me/git/error-standalone
[musl-fts]: https://github.com/void-linux/musl-fts
125 changes: 125 additions & 0 deletions containers/Dockerfile.cross-aarch64-unknown-linux-musl
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
FROM docker.io/gentoo/stage3:musl-llvm

ARG LLVM_VERSION=19

ENV PATH="/root/.cargo/bin:/usr/lib/llvm/${LLVM_VERSION}/bin:${PATH}"
# Install only aarch64 user-space wrapper when installing app-emulation/qemu.
ENV QEMU_USER_TARGETS="aarch64"
# Enable static libraries for installed packages (zstd, zlib etc.).
ENV USE="static-libs"

COPY package.accept_keywords/* /etc/portage/package.accept_keywords/
COPY package.use/* /etc/portage/package.use/

# Install QEMU, which can be used for running foreign binaries and therefore is
# useful for running `cargo test` for foreign targets.
#
# Install the following dependencies, which can be considered "build essentials"
# for the most of C/C++ software on Linux, as well as for Rust crates, which
# don't vendor C dependencies and expect them to be present in the system. All of
# these dependencies provide static libraries.
#
# * Ports of GNU, non-POSIX functionality:
# * llvm-libgcc[0] - LLVM's runtime library with GNU symbols. Many
# third-party binaries, including Rust toolchains from rustup, rely on them
# and wouldn't work with vanilla LLVM compiler runtime[1][2].
# * musl-compatible ports of glibc's GNU extensions:
# * argp-standalone
# * error-standalone
# * musl-fts (packaged as fts-standalone in Gentoo)
# * Compression libraries:
# * brotli
# * zstd
# * zlib
# * Cryptography libraries:
# * gpgme
# * OpenSSL
# * JSON
# * json-c
# * Key-value stores
# * rocksdb
# * Linux-specific libraries and utilities:
# * util-linux
# * Network libraries:
# * c-ares
# * libcurl
# * Regular expressions:
# * libpcre2
#
# Create a cross sysroot using crossdev[3], which also creates wrappers for:
# * clang, which can be used for compiling C/C++ projects without doing the
# whole dance with `--target` and `--sysroot` arguments.
# * emerge, which let you install packages in the cross sysroot.
#
# Unpack the stage3 tarball into that sysroot to avoiding compilation of the
# whole base system from scratch. Otherwise, bootstraping the base sysroot with
# `emerge-aarch64-unknown-linux-musl @system` would take an eternity to run on
# free GitHub runners.
#
# [0] https://github.com/llvm/llvm-project/tree/main/llvm-libgcc
# [1] https://github.com/rust-lang/rust/issues/119504
# [2] https://github.com/rust-lang/rustup/issues/2213#issuecomment-1888615413
# [3] https://wiki.gentoo.org/wiki/Crossdev
RUN emerge-webrsync \
&& emerge \
app-arch/brotli \
app-arch/zstd \
app-crypt/gpgme \
app-emulation/qemu \
app-eselect/eselect-repository \
dev-libs/json-c \
dev-libs/libpcre2 \
dev-libs/openssl \
dev-libs/rocksdb \
llvm-runtimes/libgcc \
net-dns/c-ares \
net-misc/curl \
sys-apps/util-linux \
sys-devel/crossdev \
sys-libs/argp-standalone \
sys-libs/error-standalone \
sys-libs/fts-standalone \
sys-libs/zlib \
&& eselect repository create crossdev \
&& crossdev --llvm --target aarch64-unknown-linux-musl --c ${LLVM_VERSION} \
&& curl -L "https://ftp-osl.osuosl.org/pub/gentoo/releases/arm64/autobuilds/current-stage3-arm64-musl-llvm/$(\
curl -L "https://ftp-osl.osuosl.org/pub/gentoo/releases/arm64/autobuilds/current-stage3-arm64-musl-llvm/latest-stage3-arm64-musl-llvm.txt" | \
grep tar.xz | cut -d ' ' -f 1)" | \
tar -xJpf - -C /usr/aarch64-unknown-linux-musl --exclude=dev --skip-old-files \
&& ln -s \
/etc/portage/repos.conf \
/usr/aarch64-unknown-linux-musl/etc/portage/repos.conf \
&& ln -s \
/etc/portage/package.accept_keywords \
/usr/aarch64-unknown-linux-musl/etc/portage/package.accept_keywords \
&& PORTAGE_CONFIGROOT=/usr/aarch64-unknown-linux-musl eselect profile set \
default/linux/arm64/23.0/musl/llvm \
&& aarch64-unknown-linux-musl-emerge --sync --quiet \
&& aarch64-unknown-linux-musl-emerge \
app-arch/brotli \
app-arch/zstd \
app-crypt/gpgme \
dev-libs/json-c \
dev-libs/libpcre2 \
dev-libs/openssl \
dev-libs/rocksdb \
llvm-runtimes/libgcc \
net-dns/c-ares \
net-misc/curl \
sys-apps/util-linux \
sys-libs/argp-standalone \
sys-libs/error-standalone \
sys-libs/fts-standalone \
sys-libs/zlib \
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
&& rustup toolchain install stable --component rust-src \
&& rustup toolchain install beta --component rust-src \
&& rustup toolchain install nightly --component rust-src \
&& rustup target add aarch64-unknown-linux-musl \
&& rustup +beta target add aarch64-unknown-linux-musl \
&& rustup +nightly target add aarch64-unknown-linux-musl \
&& cargo install btfdump \
&& rm -rf \
/var/cache/binpkgs/* \
/var/cache/distfiles/* \
/var/tmp/portage/*
Loading

0 comments on commit b93068d

Please sign in to comment.