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