musllvm

musl and LLVM cross-toolchain build system
git clone https://git.daat.foo/repos/musllvm.git
Log | Files | Refs | README | LICENSE

build.sh (12874B)


      1 #!/bin/sh
      2 set -eu
      3 
      4 . "$(dirname "$0")/config.sh"
      5 
      6 TOOLS='clang clang++ ld.lld llvm-ar llvm-ranlib llvm-nm llvm-objcopy llvm-objdump llvm-readelf llvm-strip llvm-size llvm-strings llvm-addr2line llvm-cxxfilt'
      7 
      8 doctor()
      9 {
     10 	for program in make cmake ninja python3 perl tar xz curl sha256sum rsync; do
     11 		need "$program"
     12 	done
     13 	need "$HOST_CC"
     14 	need "$HOST_CXX"
     15 	if [ "$LLVM_MODE" = system ]; then
     16 		"$HOST_CC" --version | head -n 1 | awk '/clang/ { found=1 } END { exit !found }' ||
     17 			die "LLVM_MODE=system requires Clang; set HOST_CC=clang"
     18 		clang_major=$("$HOST_CC" -dumpversion)
     19 		clang_major=${clang_major%%.*}
     20 		[ "$clang_major" -ge 19 ] 2>/dev/null || die "LLVM_MODE=system requires Clang 19 or newer"
     21 		for program in $TOOLS llvm-config; do need "$program"; done
     22 		llvm_cmake=$(llvm-config --cmakedir)
     23 		[ -f "$llvm_cmake/LLVMConfig.cmake" ] || die "LLVM CMake metadata missing; install the matching LLVM development package"
     24 	fi
     25 	printf 'Host dependencies OK (%s LLVM, %s target, %s jobs)\n' "$LLVM_MODE" "$TARGET" "$JOBS"
     26 }
     27 
     28 configure_llvm()
     29 {
     30 	cmake -S "$LLVM_SOURCE/llvm" -B "$BUILD_DIR/llvm" -G Ninja \
     31 		-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$PREFIX/host" \
     32 		-DCMAKE_C_COMPILER="$BOOTSTRAP_CC" -DCMAKE_CXX_COMPILER="$BOOTSTRAP_CXX" \
     33 		-DCMAKE_C_FLAGS="$HOST_CFLAGS" -DCMAKE_CXX_FLAGS="$HOST_CXXFLAGS" \
     34 		-DCMAKE_EXE_LINKER_FLAGS="$HOST_LDFLAGS" \
     35 		-DLLVM_ENABLE_PROJECTS='clang;lld' -DLLVM_TARGETS_TO_BUILD='X86;AArch64' \
     36 		-DLLVM_DISTRIBUTION_COMPONENTS='clang;clang-resource-headers;lld;llvm-ar;llvm-ranlib;llvm-nm;llvm-objcopy;llvm-objdump;llvm-readelf;llvm-strip;llvm-size;llvm-strings;llvm-addr2line;llvm-cxxfilt' \
     37 		-DLLVM_PARALLEL_LINK_JOBS="$LINK_JOBS" -DLLVM_ENABLE_ASSERTIONS=OFF \
     38 		-DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF \
     39 		-DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_ENABLE_BINDINGS=OFF \
     40 		-DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBXML2=OFF \
     41 		-DLLVM_ENABLE_LIBEDIT=OFF -DCLANG_ENABLE_ARCMT=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF
     42 }
     43 
     44 done_stage()
     45 {
     46 	[ -f "$BUILD_DIR/stamps/$1" ]
     47 }
     48 
     49 mark_stage()
     50 {
     51 	touch "$BUILD_DIR/stamps/$1"
     52 }
     53 
     54 toolchain()
     55 {
     56 	if [ "$LLVM_MODE" = source ]; then
     57 		if ! done_stage llvm; then
     58 			configure_llvm
     59 			cmake --build "$BUILD_DIR/llvm" --parallel "$JOBS" --target distribution
     60 			cmake --build "$BUILD_DIR/llvm" --target install-distribution
     61 			mark_stage llvm
     62 		fi
     63 		HOST_CLANG="$PREFIX/host/bin/clang"
     64 		HOST_CLANGXX="$PREFIX/host/bin/clang++"
     65 		HOST_LLD="$PREFIX/host/bin/ld.lld"
     66 		LLVM_CMAKE_DIR="$BUILD_DIR/llvm/lib/cmake/llvm"
     67 	else
     68 		HOST_CLANG=$BOOTSTRAP_CC
     69 		HOST_CLANGXX=$BOOTSTRAP_CXX
     70 		HOST_LLD=$(command -v ld.lld)
     71 		LLVM_CMAKE_DIR=$(llvm-config --cmakedir)
     72 	fi
     73 	HOST_RESOURCE=$("$HOST_CLANG" -print-resource-dir)
     74 	RESOURCE_VERSION=${HOST_RESOURCE##*/}
     75 	case "$RESOURCE_VERSION" in
     76 	*[!0-9.]*|'') die "unexpected Clang resource directory: $HOST_RESOURCE" ;;
     77 	esac
     78 	RESOURCE_DIR="$PREFIX/lib/clang/$RESOURCE_VERSION"
     79 	mkdir -p "$PREFIX/libexec" "$RESOURCE_DIR/lib/linux" "$SYSROOT/usr/lib" "$BUILD_DIR/empty-gcc"
     80 	for program in $TOOLS; do
     81 		if [ "$LLVM_MODE" = source ]; then
     82 			tool_path="../host/bin/$program"
     83 		else
     84 			case "$program" in
     85 			clang) tool_path=$HOST_CLANG ;;
     86 			clang++) tool_path=$HOST_CLANGXX ;;
     87 			*) tool_path=$(command -v "$program") ;;
     88 			esac
     89 		fi
     90 		ln -sf "$tool_path" "$PREFIX/libexec/$program"
     91 	done
     92 	if ! done_stage toolchain; then
     93 		cp -R "$HOST_RESOURCE/include" "$RESOURCE_DIR/"
     94 		mark_stage toolchain
     95 	fi
     96 }
     97 
     98 configure_musl()
     99 {
    100 	mkdir -p "$BUILD_DIR/musl"
    101 	cat > "$BUILD_DIR/musl-cc" <<EOF
    102 #!/bin/sh
    103 exec "$HOST_CLANG" --target="$TARGET" --sysroot="$SYSROOT" \\
    104 	--gcc-toolchain="$BUILD_DIR/empty-gcc" -resource-dir="$RESOURCE_DIR" \\
    105 	--ld-path="$HOST_LLD" --rtlib=compiler-rt --unwindlib=none "\$@"
    106 EOF
    107 	chmod 755 "$BUILD_DIR/musl-cc"
    108 	(
    109 		cd "$BUILD_DIR/musl"
    110 		CC="$BUILD_DIR/musl-cc" \
    111 		AR="$PREFIX/libexec/llvm-ar" RANLIB="$PREFIX/libexec/llvm-ranlib" \
    112 		CFLAGS="$TARGET_CFLAGS" LIBCC="$RESOURCE_DIR/lib/linux/libclang_rt.builtins-$ARCH.a" \
    113 		"$MUSL_SOURCE/configure" --target="$TARGET" --prefix=/usr --syslibdir=/lib \
    114 			--disable-wrapper
    115 	)
    116 }
    117 
    118 headers()
    119 {
    120 	if ! done_stage headers; then
    121 		mkdir -p "$SYSROOT/usr/include" "$BUILD_DIR/linux"
    122 		make -C "$LINUX_SOURCE" O="$BUILD_DIR/linux" ARCH="$KERNEL_ARCH" \
    123 			HOSTCC="$BOOTSTRAP_CC" INSTALL_HDR_PATH="$SYSROOT/usr" headers_install
    124 		configure_musl
    125 		make -C "$BUILD_DIR/musl" -j "$JOBS" DESTDIR="$SYSROOT" install-headers
    126 		mark_stage headers
    127 	fi
    128 }
    129 
    130 target_cmake()
    131 {
    132 	cmake -G Ninja -DCMAKE_BUILD_TYPE=Release \
    133 		-DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR="$ARCH" \
    134 		-DCMAKE_C_COMPILER="$HOST_CLANG" -DCMAKE_CXX_COMPILER="$HOST_CLANGXX" \
    135 		-DCMAKE_ASM_COMPILER="$HOST_CLANG" -DCMAKE_C_COMPILER_TARGET="$TARGET" \
    136 		-DCMAKE_CXX_COMPILER_TARGET="$TARGET" -DCMAKE_ASM_COMPILER_TARGET="$TARGET" \
    137 		-DCMAKE_SYSROOT="$SYSROOT" -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
    138 		-DCMAKE_C_FLAGS="$TARGET_CFLAGS --gcc-toolchain='$BUILD_DIR/empty-gcc' -resource-dir='$RESOURCE_DIR'" \
    139 		-DCMAKE_CXX_FLAGS="$TARGET_CFLAGS --gcc-toolchain='$BUILD_DIR/empty-gcc' -resource-dir='$RESOURCE_DIR'" \
    140 		-DCMAKE_ASM_FLAGS="--gcc-toolchain='$BUILD_DIR/empty-gcc' -resource-dir='$RESOURCE_DIR'" \
    141 		-DCMAKE_EXE_LINKER_FLAGS="--ld-path='$HOST_LLD' --rtlib=compiler-rt --unwindlib=none -resource-dir='$RESOURCE_DIR'" \
    142 		-DCMAKE_SHARED_LINKER_FLAGS="--ld-path='$HOST_LLD' --rtlib=compiler-rt --unwindlib=none -resource-dir='$RESOURCE_DIR'" \
    143 		-DCMAKE_MODULE_LINKER_FLAGS="--ld-path='$HOST_LLD' --rtlib=compiler-rt --unwindlib=none -resource-dir='$RESOURCE_DIR'" \
    144 		-DCMAKE_AR="$PREFIX/libexec/llvm-ar" -DCMAKE_RANLIB="$PREFIX/libexec/llvm-ranlib" \
    145 		-DCMAKE_NM="$PREFIX/libexec/llvm-nm" -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF "$@"
    146 }
    147 
    148 builtins()
    149 {
    150 	if ! done_stage builtins; then
    151 		target_cmake -S "$LLVM_SOURCE/compiler-rt" -B "$BUILD_DIR/builtins" \
    152 			-DLLVM_CMAKE_DIR="$LLVM_CMAKE_DIR" \
    153 			-DCMAKE_INSTALL_PREFIX="$RESOURCE_DIR" -DCOMPILER_RT_INSTALL_PATH="$RESOURCE_DIR" \
    154 			-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \
    155 			-DCOMPILER_RT_BUILD_BUILTINS=ON -DCOMPILER_RT_EXCLUDE_ATOMIC_BUILTIN=ON \
    156 			-DCOMPILER_RT_BUILD_CRT=ON -DCOMPILER_RT_BUILD_SANITIZERS=OFF \
    157 			-DCOMPILER_RT_BUILD_XRAY=OFF -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
    158 			-DCOMPILER_RT_BUILD_PROFILE=OFF -DCOMPILER_RT_BUILD_MEMPROF=OFF \
    159 			-DCOMPILER_RT_BUILD_CTX_PROFILE=OFF -DCOMPILER_RT_BUILD_ORC=OFF \
    160 			-DCOMPILER_RT_BUILD_GWP_ASAN=OFF -DCOMPILER_RT_INCLUDE_TESTS=OFF
    161 		cmake --build "$BUILD_DIR/builtins" --parallel "$JOBS"
    162 		cmake --install "$BUILD_DIR/builtins"
    163 		mark_stage builtins
    164 	fi
    165 }
    166 
    167 musl()
    168 {
    169 	if ! done_stage musl; then
    170 		builtins_archive=$("$HOST_CLANG" --target="$TARGET" -resource-dir="$RESOURCE_DIR" \
    171 			--rtlib=compiler-rt -print-libgcc-file-name)
    172 		[ -f "$builtins_archive" ] || die "compiler-rt archive not found: $builtins_archive"
    173 		make -C "$BUILD_DIR/musl" -j "$JOBS" "LIBCC=$builtins_archive"
    174 		make -C "$BUILD_DIR/musl" "LIBCC=$builtins_archive" DESTDIR="$SYSROOT" install
    175 		ln -sf ../usr/lib/libc.so "$SYSROOT/lib/ld-musl-$ARCH.so.1"
    176 		mark_stage musl
    177 	fi
    178 }
    179 
    180 atomic()
    181 {
    182 	if ! done_stage atomic; then
    183 		mkdir -p "$BUILD_DIR/atomic"
    184 		cp "$ATOMIC_SOURCE/atomic.c" "$ATOMIC_SOURCE/Makefile" "$BUILD_DIR/atomic/"
    185 		make -C "$BUILD_DIR/atomic" -j "$JOBS" CC="$BUILD_DIR/musl-cc" \
    186 			AR="$PREFIX/libexec/llvm-ar" CFLAGS="$TARGET_CFLAGS"
    187 		make -C "$BUILD_DIR/atomic" CC="$BUILD_DIR/musl-cc" \
    188 			AR="$PREFIX/libexec/llvm-ar" CFLAGS="$TARGET_CFLAGS" PREFIX=/usr DESTDIR="$SYSROOT" install
    189 		mark_stage atomic
    190 	fi
    191 }
    192 
    193 runtimes()
    194 {
    195 	if ! done_stage runtimes; then
    196 		# musl lacks __cxa_thread_atexit_impl
    197 		# compile only probes cannot detect missing symbols
    198 		# typeinfo comparison 2 also compares names, as libstdc++ does (chimera)
    199 		target_cmake -S "$LLVM_SOURCE/runtimes" -B "$BUILD_DIR/runtimes" \
    200 			-DCMAKE_INSTALL_PREFIX=/usr -DLLVM_ENABLE_RUNTIMES='libunwind;libcxxabi;libcxx' \
    201 			-DLIBCXX_HAS_MUSL_LIBC=ON -DLIBCXX_USE_COMPILER_RT=ON \
    202 			-DLIBCXX_TYPEINFO_COMPARISON_IMPLEMENTATION=2 \
    203 			-DLIBCXX_CXX_ABI=libcxxabi -DLIBCXX_ENABLE_SHARED=ON -DLIBCXX_ENABLE_STATIC=ON \
    204 			-DLIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY=ON \
    205 			-DLIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY=OFF -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT=ON \
    206 			-DLIBCXX_INCLUDE_TESTS=OFF -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
    207 			-DLIBCXXABI_USE_COMPILER_RT=ON -DLIBCXXABI_USE_LLVM_UNWINDER=ON \
    208 			-DLIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL=OFF \
    209 			-DLIBCXXABI_ENABLE_SHARED=ON -DLIBCXXABI_ENABLE_STATIC=ON \
    210 			-DLIBCXXABI_INCLUDE_TESTS=OFF -DLIBUNWIND_USE_COMPILER_RT=ON \
    211 			-DLIBUNWIND_ENABLE_SHARED=ON -DLIBUNWIND_ENABLE_STATIC=ON \
    212 			-DLIBUNWIND_INCLUDE_TESTS=OFF
    213 		cmake --build "$BUILD_DIR/runtimes" --parallel "$JOBS"
    214 		DESTDIR="$SYSROOT" cmake --install "$BUILD_DIR/runtimes"
    215 		mark_stage runtimes
    216 	fi
    217 }
    218 
    219 wrappers()
    220 {
    221 	mkdir -p "$PREFIX/bin" "$PREFIX/share/musllvm/empty-gcc"
    222 	for pair in cc:cc.sh c++:cc.sh ld:ld.sh pkg-config:pkg-config.sh env:env.sh; do
    223 		wrapper="$PREFIX/bin/$TARGET-${pair%%:*}"
    224 		sed -e "s/@TARGET@/$TARGET/g" -e "s/@RESOURCE_VERSION@/$RESOURCE_VERSION/g" \
    225 			"$PROOT/files/toolchain/${pair#*:}" > "$wrapper"
    226 		chmod 755 "$wrapper"
    227 	done
    228 	for alias in clang gcc; do ln -sf "$TARGET-cc" "$PREFIX/bin/$TARGET-$alias"; done
    229 	for alias in clang++ g++; do ln -sf "$TARGET-c++" "$PREFIX/bin/$TARGET-$alias"; done
    230 	for pair in ar:ar ranlib:ranlib nm:nm objcopy:objcopy objdump:objdump \
    231 		readelf:readelf strip:strip size:size strings:strings addr2line:addr2line c++filt:cxxfilt; do
    232 		ln -sf "../libexec/llvm-${pair#*:}" "$PREFIX/bin/$TARGET-${pair%%:*}"
    233 	done
    234 	sed -e "s/@TARGET@/$TARGET/g" -e "s/@ARCH@/$ARCH/g" \
    235 		"$PROOT/files/toolchain/toolchain.cmake" > "$PREFIX/share/musllvm/toolchain.cmake"
    236 	sed -e "s/@TARGET@/$TARGET/g" -e "s/@RESOURCE_VERSION@/$RESOURCE_VERSION/g" \
    237 		"$PROOT/files/toolchain/clang.cfg" > "$PREFIX/share/musllvm/clang.cfg"
    238 	printf '%s\n' "$RESOURCE_VERSION" > "$PREFIX/share/musllvm/resource-version"
    239 	printf 'Toolchain ready: %s\n' "$PREFIX"
    240 }
    241 
    242 stage=${1:-all}
    243 case "$stage" in
    244 config)
    245 	printf 'ARCH=%s\nTARGET=%s\nLLVM_MODE=%s\nPREFIX=%s\nSYSROOT=%s\nBUILD_DIR=%s\nJOBS=%s\n' \
    246 		"$ARCH" "$TARGET" "$LLVM_MODE" "$PREFIX" "$SYSROOT" "$BUILD_DIR" "$JOBS"
    247 	exit ;;
    248 doctor) doctor; exit ;;
    249 clean)
    250 	[ ! -d "$BUILD_DIR/lock" ] || die "build is running: $BUILD_DIR/lock"
    251 	[ -f "$PREFIX/.musllvm-config" ] || [ ! -e "$PREFIX" ] || die "refusing to remove an unmarked PREFIX: $PREFIX"
    252 	[ -f "$BUILD_DIR/config" ] || [ ! -e "$BUILD_DIR" ] || die "refusing to remove an unmarked BUILD_DIR: $BUILD_DIR"
    253 	rm -rf "$PREFIX" "$BUILD_DIR"
    254 	exit ;;
    255 all|headers|builtins|musl|atomic|runtimes|toolchain|configure-llvm) ;;
    256 *) die "unknown build stage: $stage" ;;
    257 esac
    258 
    259 doctor
    260 unset CPATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH OBJC_INCLUDE_PATH LIBRARY_PATH
    261 unset GCC_EXEC_PREFIX COMPILER_PATH CFLAGS CXXFLAGS CPPFLAGS LDFLAGS
    262 BOOTSTRAP_CC=$(absolute_path "$(command -v "$HOST_CC")")
    263 BOOTSTRAP_CXX=$(absolute_path "$(command -v "$HOST_CXX")")
    264 sh "$PROOT/scripts/check.sh" pull
    265 extract_source llvm
    266 LLVM_SOURCE=$source_path
    267 extract_source musl
    268 MUSL_SOURCE=$source_path
    269 extract_source linux
    270 LINUX_SOURCE=$source_path
    271 extract_source atomic
    272 ATOMIC_SOURCE=$source_path
    273 
    274 config_digest=$({
    275 	printf '%s\n' "$ARCH" "$TARGET" "$LLVM_MODE" "$PREFIX" "$BUILD_DIR" \
    276 		"$BOOTSTRAP_CC" "$BOOTSTRAP_CXX" "$TARGET_CFLAGS" "$HOST_CFLAGS" "$HOST_CXXFLAGS" "$HOST_LDFLAGS"
    277 	"$BOOTSTRAP_CC" --version
    278 	"$BOOTSTRAP_CXX" --version
    279 	cat "$SOURCES_FILE"
    280 	cat "$PROOT/scripts/build.sh" "$PROOT/scripts/config.sh"
    281 	if [ "$LLVM_MODE" = system ]; then
    282 		for program in $TOOLS llvm-config; do command -v "$program"; done
    283 		ld.lld --version
    284 		llvm-ar --version
    285 	fi
    286 } | sha256sum)
    287 config_digest=${config_digest%% *}
    288 for config_file in "$PREFIX/.musllvm-config" "$BUILD_DIR/config"; do
    289 	if [ -f "$config_file" ]; then
    290 		[ "$(cat "$config_file")" = "$config_digest" ] ||
    291 			die "configuration changed; run make clean with the same ARCH, PREFIX and BUILD_DIR before rebuilding"
    292 	fi
    293 done
    294 for directory in "$PREFIX" "$BUILD_DIR"; do
    295 	if [ -d "$directory" ] && [ ! -f "$directory/.musllvm-config" ] && [ ! -f "$directory/config" ]; then
    296 		[ -z "$(ls -A "$directory")" ] || die "refusing to use an unmarked nonempty directory: $directory"
    297 	fi
    298 done
    299 mkdir -p "$PREFIX" "$BUILD_DIR/stamps"
    300 mkdir "$BUILD_DIR/lock" 2>/dev/null || die "build is running: $BUILD_DIR/lock"
    301 trap 'rmdir "$BUILD_DIR/lock"' 0
    302 trap 'exit 1' HUP INT TERM
    303 printf '%s\n' "$config_digest" > "$PREFIX/.musllvm-config"
    304 printf '%s\n' "$config_digest" > "$BUILD_DIR/config"
    305 if [ "$stage" = configure-llvm ]; then configure_llvm; exit; fi
    306 toolchain
    307 if [ "$stage" = toolchain ]; then exit; fi
    308 headers
    309 if [ "$stage" = headers ]; then exit; fi
    310 builtins
    311 if [ "$stage" = builtins ]; then exit; fi
    312 musl
    313 if [ "$stage" = musl ]; then exit; fi
    314 atomic
    315 if [ "$stage" = atomic ]; then exit; fi
    316 runtimes
    317 wrappers
    318 if [ "$stage" = all ]; then sh "$PROOT/scripts/verify.sh"; fi