aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/bootstrap.sh
blob: d2d439073fe9173517409fce549cc959c33cbcb6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/bin/sh

set -e

TARGET_ARCH="$1"
SUDO_APK=abuild-apk

# optional cross build packages
: ${KERNEL_PKG=linux-firmware linux-lts}

if [ -z "$TARGET_ARCH" ]; then
	program=$(basename $0)
	cat <<EOF
usage: $program TARGET_ARCH

This script creates a local cross-compiler, and uses it to
cross-compile an Alpine Linux base system for new architecture.

Steps for introducing new architecture include:
- adding the compiler triplet and arch type to abuild
- adding the arch type detection to apk-tools
- adjusting build rules for packages that are arch aware:
  gcc, openssl, linux-headers
- create new kernel config for linux-lts

After these steps the initial cross-build can be completed
by running this with the target arch as parameter, e.g.:
	./$program aarch64

The cross-compiler generated by this script is not intended
nor supported for any use other than building the base system
and other packages in the bootstrap path.

EOF
	return 1
fi

# get abuild configurables
[ -e /usr/share/abuild/functions.sh ] || (echo "abuild not found" ; exit 1)
CBUILDROOT="$(CTARGET=$TARGET_ARCH . /usr/share/abuild/functions.sh ; echo $CBUILDROOT)"
. /usr/share/abuild/functions.sh
[ -z "$CBUILD_ARCH" ] && die "abuild is too old (use 2.29.0 or later)"
[ -z "$CBUILDROOT" ] && die "CBUILDROOT not set for $TARGET_ARCH"
export CBUILD

# deduce aports directory
[ -z "$APORTS" ] && APORTS=$(realpath $(dirname $0)/../)
[ -e "$APORTS/main/build-base" ] || die "Unable to deduce aports base checkout"

apkbuildname() {
	local repo="${1%%/*}"
	local pkg="${1##*/}"
	[ "$repo" = "$1" ] && repo="main"
	echo $APORTS/$repo/$pkg/APKBUILD
}

msg() {
	[ -n "$quiet" ] && return 0
	local prompt="$GREEN>>>${NORMAL}"
	local name="${BLUE}bootstrap-${TARGET_ARCH}${NORMAL}"
        printf "${prompt} ${name}: %s\n" "$1" >&2
}

if [ ! -d "$CBUILDROOT" ]; then
	msg "Creating sysroot in $CBUILDROOT"
	mkdir -p "$CBUILDROOT/etc/apk/keys"
	# /etc/apk/keys and ~/.abuild/ can contain files with the same names.
	# if that is the case, cp will abort copying and fail. Then on the next
	# run of the bootstrap script, 1) the keys are not in the sysroot and 
	# 2) the apk database is not initialized the sysroot
	# Thus it's unusable at that point and needs to be deleted manually.
	cp -a /etc/apk/keys/* "$CBUILDROOT/etc/apk/keys"
	cp -a ~/.abuild/*.pub "$CBUILDROOT/etc/apk/keys"
	${SUDO_APK} add --quiet --initdb --arch $TARGET_ARCH --root $CBUILDROOT
fi

msg "Building cross-compiler"

# Build and install cross binutils (--with-sysroot)
CTARGET=$TARGET_ARCH BOOTSTRAP=nobase APKBUILD=$(apkbuildname binutils) abuild -r

if ! CHOST=$TARGET_ARCH BOOTSTRAP=nolibc APKBUILD=$(apkbuildname musl) abuild up2date 2>/dev/null; then
	# C-library headers for target
	CHOST=$TARGET_ARCH BOOTSTRAP=nocc APKBUILD=$(apkbuildname musl) abuild -r

	# Minimal cross GCC
	EXTRADEPENDS_HOST="musl-dev" \
	CTARGET=$TARGET_ARCH BOOTSTRAP=nolibc APKBUILD=$(apkbuildname gcc) abuild -r

	# Cross build bootstrap C-library for the target
	EXTRADEPENDS_BUILD="gcc-pass2-$TARGET_ARCH" \
	CHOST=$TARGET_ARCH BOOTSTRAP=nolibc APKBUILD=$(apkbuildname musl) abuild -r
fi

# Full cross GCC
EXTRADEPENDS_TARGET="musl musl-dev" \
CTARGET=$TARGET_ARCH BOOTSTRAP=nobase APKBUILD=$(apkbuildname gcc) abuild -r

# Cross build-base
CTARGET=$TARGET_ARCH BOOTSTRAP=nobase APKBUILD=$(apkbuildname build-base) abuild -r

msg "Cross building base system"

# Implicit dependencies for early targets
EXTRADEPENDS_TARGET="libgcc libstdc++ musl-dev"

# On a few architectures like riscv64 we need to account for
# gcc requiring -ltomic to be set explicitly if a C[++]11 program
# uses atomics (e.g. #include <atomic>):
# https://github.com/riscv/riscv-gnu-toolchain/issues/183#issuecomment-253721765
# The reason gcc itself is needed is because .so is in that package,
# not in libatomic.
if [ "$TARGET_ARCH" = "riscv64" ]; then
	NEEDS_LIBATOMIC="yes"
fi

# ordered cross-build
for PKG in fortify-headers linux-headers musl libc-dev pkgconf zlib \
	   openssl ca-certificates libmd \
	   gmp mpfr4 mpc1 isl24 cloog libucontext binutils gcc \
	   libbsd libretls busybox busybox-initscripts make \
	   apk-tools file \
	   libcap openrc alpine-conf alpine-baselayout alpine-keys alpine-base patch build-base \
	   attr acl fakeroot tar \
	   lzip abuild ncurses libedit openssh \
	   libcap-ng util-linux libaio lvm2 popt xz \
	   json-c argon2 cryptsetup zstd kmod lddtree mkinitfs \
	   community/go libffi community/ghc \
	   brotli libev c-ares cunit nghttp2 curl \
	   pcre libssh2 \
	   libxml2 pax-utils llvm14 community/rust \
	   $KERNEL_PKG ; do

	if [ "$NEEDS_LIBATOMIC" = "yes" ]; then
		EXTRADEPENDS_BUILD="libatomic gcc-$TARGET_ARCH g++-$TARGET_ARCH"
	fi
	EXTRADEPENDS_TARGET="$EXTRADEPENDS_TARGET"  EXTRADEPENDS_BUILD="$EXTRADEPENDS_BUILD" \
	CHOST=$TARGET_ARCH BOOTSTRAP=bootimage APKBUILD=$(apkbuildname $PKG) abuild -r

	case "$PKG" in
	fortify-headers | libc-dev)
		# Additional implicit dependencies once built
		EXTRADEPENDS_TARGET="$EXTRADEPENDS_TARGET $PKG"
		;;
	gcc)
		if [ "$NEEDS_LIBATOMIC" = "yes" ]; then
			EXTRADEPENDS_TARGET="libatomic gcc $EXTRADEPENDS_TARGET"
		fi
		;;
	build-base)
		# After build-base, that alone is sufficient dependency in the target
		EXTRADEPENDS_TARGET="busybox $PKG"
		;;
	esac
done