Skip to content

Commit 4ce375b

Browse files
authored
Implement experimental support for non-containerized builds (#8)
1 parent 505f5e7 commit 4ce375b

8 files changed

Lines changed: 134 additions & 32 deletions

File tree

Dockerfile.builder

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ RUN apt-get update \
2222

2323
ARG REPO_ROOT
2424

25+
ENV TOOLCHAIN_BUILD_IN_DOCKER=1
26+
2527
COPY cmake /${REPO_ROOT}/cmake
2628
COPY scripts /${REPO_ROOT}/scripts
2729
COPY config /${REPO_ROOT}

README.md

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ The resulting toolchain is written to `./output/llvm-pauth.squashfs` - it is a
77
compressed read-only file system image which is intended to be `mount`ed to
88
`/opt/llvm-pauth`.
99

10+
Another option is building the toolchain without containers, purely on the host,
11+
but keep in mind that Clang is able to auto-discover system-provided sysroots
12+
for cross-compilation (for example, the sysroot under `/usr/aarch64-linux-gnu`
13+
which is installed as a dependency of the `gcc-aarch64-linux-gnu` package in
14+
Ubuntu running on x86_64 host). This is hopefully not an issue for these scripts,
15+
as they explicitly specify the sysroots in Clang `*.cfg` files, and everything
16+
compiles successfully in x86_64 containerized build. Anyway, containerized
17+
builds (especially, when performed on a non-AArch64 host) look like yet another
18+
layer of protection against unintentionally linking to any system-provided
19+
libraries.
20+
1021
The versions of LLVM and Musl as well as a few other tunables are set in `config`:
1122
by default, mainline `llvmorg-21.1.0-rc1` tag is used together with a patched
1223
version of Musl that can be obtained at https://github.com/access-softek/musl.
@@ -25,25 +36,25 @@ not checked whether the working copy is clean or not.
2536
Ensure `llvm-project` and `musl` repositories are cloned on the host and contain
2637
the commits specified in the `./config` file (by default, you need the mainline
2738
LLVM monorepo and patched Musl version from https://github.com/access-softek/musl).
28-
Alternatively, you can pass https:// or git:// URLs directly to `./docker.sh sources`.
39+
Alternatively, you can pass https:// or git:// URLs directly to `./build.sh sources`.
2940

3041
Checkout the particular commits of LLVM and Musl sources under `./src` and
3142
download Linux kernel tarball by running:
3243

3344
```
34-
./docker.sh sources <llvm_repo_url> <musl_repo_url>
45+
./build.sh sources <llvm_repo_url> <musl_repo_url>
3546
```
3647

3748
if LLVM and Musl are already cloned on the host, use
3849

3950
```
40-
./docker.sh sources file:///absolute/path/to/llvm-project file:///absolute/path/to/musl
51+
./build.sh sources file:///absolute/path/to/llvm-project file:///absolute/path/to/musl
4152
```
4253

4354
Then build the toolchain by running
4455

4556
```
46-
./docker.sh build
57+
./build.sh build
4758
```
4859

4960
The build artifact is `./output/llvm-pauth.squashfs` file.
@@ -118,3 +129,44 @@ $ gdb-multiarch
118129
>>> b main
119130
>>> c
120131
```
132+
133+
# Building the toolchain without Docker
134+
135+
The preferred way to use these scripts is building inside a container, though
136+
it is possible to build the toolchain purely on the host.
137+
138+
This can be achieved by running `./build.sh host-build` instead of
139+
`./build.sh build` after checking out the sources to `./src/*` the same way as
140+
for a containerized build. After a successful build, the toolchain is installed
141+
to the `./inst` subdirectory inside this repository (can be customized in
142+
`./config`) and no archive is created under `./output`.
143+
144+
When the toolchain is being built inside a container, a temporary volume is
145+
attached as a build directory, which is discarded when the container is stopped,
146+
thus the subsequent invocation of `./build.sh build` uses a fresh build directory
147+
automatically. When building on the host, on the other hand, the user is
148+
responsible for removing the half-built subdirectories of the main build
149+
directory after investigating the errors.
150+
151+
Inside the main build directory (which is `./build` by default), each build step
152+
corresponds to `{build step}-{target triple}` subdirectory (it depends on the
153+
particular build step, whether this subdirectory is created or not) and a
154+
corresponding stamp file with `.stamp` suffix (which is always created **after**
155+
the build step finishes successfully):
156+
* the step is skipped if corresponding stamp file exists (whether the directory
157+
exists or not)
158+
* an error is reported if a half-built subdirectory exists without a stamp file
159+
indicating a successful build
160+
* otherwise, a build step is performed and the stamp file is `touch`ed on
161+
success
162+
163+
Please note that each step is performed without taking the dependencies into
164+
account, thus the user is responsible for removing the subdirectories
165+
corresponding to the steps that have to be redone after some other steps (such
166+
as rebuilding everything linking to `crt1.o` after the start files were
167+
rebuilt). It is generally possible to simply remove the entire `./build` and
168+
`./inst` subdirectories, since LLVM should be rebuilt rather quickly thanks to
169+
ccache - the main reason for not removing `./build` automatically, aside from
170+
simplifying the debugging in case of errors, is not to run `rm -rf` with
171+
computed paths, as this can be harmful to the host system in case of
172+
misconfiguration.

docker.sh renamed to build.sh

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@ ROOT="$(dirname "$0")"
44
ROOT="$(realpath "$ROOT")"
55
cd "$ROOT"
66

7-
# Path inside the container.
8-
REPO_ROOT=/repo
9-
10-
. ./config
11-
. ./scripts/global-vars
12-
137
check_repo_sha() {
148
local repo_path="$1"
159
local expected_sha="$2"
@@ -27,6 +21,9 @@ check_repo_sha() {
2721
}
2822

2923
fetch_sources() {
24+
. ./config
25+
. ./scripts/global-vars
26+
3027
if [ "$#" != 2 ]; then
3128
echo "Usage: docker.sh sources <llvm_repo_url> <musl_repo_url>"
3229
echo "Note that file:///path/to/repo/ URLs can be used."
@@ -48,7 +45,12 @@ fetch_sources() {
4845
check_repo_sha "$ROOT/src/musl" "$MUSL_SHA"
4946
}
5047

51-
build_toolchain() {
48+
build_in_docker() {
49+
# Path inside the container.
50+
REPO_ROOT=/repo
51+
. ./config
52+
. ./scripts/global-vars
53+
5254
check_repo_sha "$ROOT/src/llvm" "$LLVM_SHA"
5355
check_repo_sha "$ROOT/src/musl" "$MUSL_SHA"
5456

@@ -66,6 +68,17 @@ build_toolchain() {
6668
"$DOCKER_IMAGE_NAME" "$REPO_ROOT/scripts/build-in-docker.sh"
6769
}
6870

71+
build_on_host() {
72+
REPO_ROOT="$ROOT"
73+
. ./config
74+
. ./scripts/global-vars
75+
76+
check_repo_sha "$ROOT/src/llvm" "$LLVM_SHA"
77+
check_repo_sha "$ROOT/src/musl" "$MUSL_SHA"
78+
79+
./scripts/build-on-host.sh
80+
}
81+
6982
main() {
7083
local subcmd="$1"
7184

@@ -75,11 +88,14 @@ main() {
7588
fetch_sources "$@"
7689
;;
7790
build)
78-
build_toolchain
91+
build_in_docker
92+
;;
93+
host-build)
94+
build_on_host
7995
;;
8096
*)
8197
echo "Unknown subcommand: $subcmd"
82-
echo "Expected: 'sources', 'build'."
98+
echo "Expected one of 'sources', 'build', or 'host-build' (experimental)."
8399
exit 1
84100
;;
85101
esac

config

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ EXTRA_FLAGS_MUSL=""
1919
BUILD_OPTIMIZED_RUNTIMES=yes
2020

2121
# Toolchain installation prefix.
22+
2223
# This path is used inside the container as a temporary location to install
2324
# the toolchain before packing it into a SquashFS image.
24-
INSTALL_DIR=/opt/llvm-pauth
25+
DOCKER_BUILD_INSTALL_DIR=/opt/llvm-pauth
26+
27+
# This path is used as a final installation prefix when building the toolchain
28+
# on the host.
29+
HOST_BUILD_INSTALL_DIR="$REPO_ROOT/inst"
2530

2631
# Command to build and run containers on the host: docker, podman, etc.
2732
DOCKER_CMD=docker

scripts/build-all.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
set -e
33

44
# This script invokes all other build-*.sh scripts.
5-
# Inside the container, it is called by build-in-docker.sh.
6-
# FIXME: Add another entry point script to perform build directly on host.
5+
# It is called either by build-in-docker.sh or by build-on-host.sh, depending
6+
# on whether the build is performed inside a container.
7+
# The calling script is responsible for `export`ing REPO_ROOT environment
8+
# variable (as explained in ./global-vars).
79

810
set -x
911
cd "$(dirname "$0")"

scripts/build-in-docker.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ cd "$(dirname "$0")"
55
# This script is an entry point inside the Docker container.
66
# Its location is expected to be $REPO_ROOT/scripts/build-in-docker.sh.
77

8-
# Export the REPO_ROOT variable, so it can be used by the 'config' script
9-
# sourced by this script and its subprocesses.
8+
# Export the REPO_ROOT variable, so it can be used by the 'global-vars' script
9+
# sourced by this script, as well as its subprocesses.
1010
export REPO_ROOT="$(pwd)/.."
1111
. ../config
1212
. ./global-vars
1313

14-
./build-all.sh
14+
if ! ./build-all.sh; then
15+
echo "Containerized build failed - $BUILD_TMP is discarded automatically."
16+
exit 1
17+
fi
1518

1619
# It is important to remove an old image, if any, as otherwise it would be
1720
# appended to instead of overwritten!

scripts/build-on-host.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env sh
2+
set -e
3+
cd "$(dirname "$0")"
4+
5+
# This script starts the build on the host, similar to build-in-docker.sh for a
6+
# containeraized build.
7+
# Its location is expected to be $REPO_ROOT/scripts/build-on-host.sh.
8+
9+
# Export the REPO_ROOT variable, so it can be used by the 'global-vars' script
10+
# sourced by this script, as well as its subprocesses.
11+
export REPO_ROOT="$(pwd)/.."
12+
. ../config
13+
. ./global-vars
14+
15+
./build-all.sh
16+
17+
echo "Build finished, the toolchain is installed to $INSTALL_DIR."

scripts/global-vars

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,27 @@
1313
# NB: Make sure the path is absolute, as relative paths may be interpreted in
1414
# surprising ways in some contexts, such as in the argument of `--toolchain`
1515
# option of CMake.
16-
if [ "x$REPO_ROOT" = "x" ]; then
17-
echo "error: REPO_ROOT variable should be set before sourcing 'config'." 1>&2
18-
exit 1
19-
fi
16+
if [ "x$REPO_ROOT" != "x" ]; then
17+
# Directories mounted from the host to the container in Docker-based build.
18+
# When Docker is used, these path should be as seen from inside the container,
19+
# otherwise they are just absolute paths on the host pointing inside this repo.
20+
OUTPUT_DIR="$REPO_ROOT/output"
21+
CCACHE_DIR="$REPO_ROOT/ccache"
22+
BUILD_TMP="$REPO_ROOT/build"
2023

21-
# Directories mounted from the host to the container
22-
# (as seen from inside the container).
23-
OUTPUT_DIR="$REPO_ROOT/output"
24-
CCACHE_DIR="$REPO_ROOT/ccache"
25-
BUILD_TMP="$REPO_ROOT/build"
24+
SRC_DIR="$REPO_ROOT/src"
25+
LLVM_SOURCE_DIR="$SRC_DIR/llvm"
26+
MUSL_SOURCE_DIR="$SRC_DIR/musl"
2627

27-
SRC_DIR="$REPO_ROOT/src"
28-
LLVM_SOURCE_DIR="$SRC_DIR/llvm"
29-
MUSL_SOURCE_DIR="$SRC_DIR/musl"
28+
CMAKE_DIR="$REPO_ROOT/cmake"
29+
fi
3030

31-
CMAKE_DIR="$REPO_ROOT/cmake"
31+
# TOOLCHAIN_BUILD_IN_DOCKER is defined in Dockerfile.builder
32+
if [ -n "$TOOLCHAIN_BUILD_IN_DOCKER" ]; then
33+
INSTALL_DIR="$DOCKER_BUILD_INSTALL_DIR"
34+
else
35+
INSTALL_DIR="$HOST_BUILD_INSTALL_DIR"
36+
fi
3237

3338
# Linux kernel version to be used to provide user-space headers to libc.
3439
# Any recent version should work, so this variable is defined here instead

0 commit comments

Comments
 (0)