#!/usr/bin/env bash LIB_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/../../lib" source "${LIB_DIR}/ossbuild-common.bash" ossbuild-prepare-llvm() { echo "[INF] ${FUNCNAME[0]} ..." local version="${1}" local repository="https://github.com/llvm/llvm-project" local archive="llvm-project-${version}.src.tar.xz" pushd "${HOME}/work/oss" &> /dev/null [ -e "${archive}" ] || { wget "${repository}/releases/download/llvmorg-${version}/${archive}" } local src_dir src_dir="${HOME}/work/oss/$(basename "${archive}" .tar.xz)" [ -d "${src_dir}" ] || tar xvf "${archive}" popd &> /dev/null pushd "${src_dir}" &> /dev/null } ossbuild-generate-llvm() { echo "[INF] ${FUNCNAME[0]} ..." local build_type="${1:-Release}" local projects="${2:-}" local prefix="${3:-}" local source_dir='llvm' [ -d "${source_dir}" ] || { echo "[ERR] could not find source dir: '${source_dir}'" return 1 } local -a cmake_extra_args local build_dir="build-${build_type,,}" if [ -n "${projects}" ]; then build_dir="${build_dir}.${projects//;/.}" else # get runtime target from GCC local gcc_runtime_target gcc_runtime_target="$(gcc -xc /dev/null -v -E 2>&1 | grep '^Target:' | cut -d' ' -f2)" #projects='clang;clang-tools-extra;lld;lldb;compiler-rt;libunwind;libcxx;libcxxabi' projects='clang;clang-tools-extra;compiler-rt;lld;lldb' cmake_extra_args+=( -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" # must match triple of GCC used for first build step (here: # /opt/lib/gcc/x86_64-openwrt-linux-gnu/8.4.0) -DLLVM_RUNTIME_TARGETS="${gcc_runtime_target}" # required )in case of a non-standard triple) in order to allow the # final clang to find the GCC resource dir which contains files # required by the linker (like crti.o, libgcc, libc_nonshared.a in # e.g. /opt/lib/gcc/x86_64-openwrt-linux-gnu/8.4.0) # # Note: This also sets LLVM_DEFAULT_TARGET_TRIPLE. -DLLVM_HOST_TRIPLE="${gcc_runtime_target}" # Avoid error during cmake toolchain generation: # # ERROR: Compiler doesn't support generation of unwind tables if # exception support is disabled. Building libunwind DSO with runtime # dependency on C++ ABI library is not supported. -DRUNTIMES_${gcc_runtime_target}_LIBUNWIND_ENABLE_SHARED="Off" ) fi local log="${build_dir/build-/cmake-}.log" if [ -z "${prefix}" ]; then if is-qnap-qts; then prefix="$(readlink /opt)/local" else prefix='/usr/local' fi fi if [ -d "${build_dir}" ]; then if [ -v FORCE_REGENERATE ]; then if [ "${FORCE_REGENERATE}" != "no-rm" ]; then rm -rf "${build_dir}" fi else echo "[INF] keep existing build dir: '${build_dir}'" return 0 fi fi local -a cmd=( cmake -S "${source_dir}" -B "${build_dir}" -G Ninja -DCMAKE_BUILD_TYPE="${build_type}" -DCMAKE_INSTALL_PREFIX="${prefix}" -DLLVM_ENABLE_PROJECTS="${projects}" #-DCMAKE_CXX_LINK_FLAGS="-Wl,-rpath,\$ORIGIN/../lib -L${prefix}/lib" -DCMAKE_CXX_LINK_FLAGS="-Wl,-rpath,${prefix}/lib -L${prefix}/lib -L/opt/lib" "${cmake_extra_args[@]}" ) if command -v ccache &> /dev/null; then cmd+=( -DLLVM_CCACHE_BUILD="ON" -DLLVM_CCACHE_PARAMS="CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_BASEDIR='${PWD}/${build_dir}'" ) fi if is-qnap-qts; then # get gcc_install_prefix local gcc_install_prefix gcc_install_prefix="$(gcc -x c -E /dev/null -v 2>&1 | rg 'Configured with' | sed -e 's/.* --prefix=\([^ ]\+\).*/\1/')" cmd+=( # enable clang to locate the GCC toolchain if it is in a # non-standard location (like ~/opt~ on QNAP Entware) -DGCC_INSTALL_PREFIX="${gcc_install_prefix}" # CMAKE_EXE_LINKER_FLAGS and LLVM_PARALLEL_LINK_JOBS flags reduce linker # memory usage (without them, llvm build fails on qnas) -DCMAKE_EXE_LINKER_FLAGS="-Wl,--reduce-memory-overheads -Wl,--hash-size=1021" -DLLVM_PARALLEL_LINK_JOBS=1 # avoid cmake warning: 'runtime library [libz.so.1] in /lib may be # hidden by files in ...' -DZLIB_ROOT="${prefix}" ) # From https://lists.llvm.org/pipermail/cfe-users/2016-November/001076.html: # # ... gcc's configure file comes with handy flags like: # - --with-local-prefix= which allows to override the default system # header include /usr/local/include # - --with-native-system-header-dir= to override the default system # header include /include # - --enable-linker-build-id to enable build ids by default (in clang # the equivalent is -DENABLE_LINKER_BUILD_ID) # - --with-linker-hash-style=gnu to force the gnu hash style when # emitting the symbol table. # # However, LLVM/clang doesn't seem to provide an equivalent for # --with-local-prefix, --with-native-system-header-dir and # --with-linker-hash-style. So instead, some sources need to be # patched. echo local clang_toolchain_src clang_toolchain_src='clang/lib/Driver/ToolChains/Linux.cpp' [ -e "${clang_toolchain_src}.orig" ] || { echo "[INF] patching ${clang_toolchain_src} ..." sed -i.orig \ -e 's,/usr/,/opt/,g' "${clang_toolchain_src}" } echo "[INF] using patch:" diff -u "${clang_toolchain_src}"{.orig,} || true echo "[INF] end of patch (${clang_toolchain_src})" echo clang_toolchain_src='clang/lib/Driver/ToolChains/Gnu.cpp' [ -e "${clang_toolchain_src}.orig" ] || { echo "[INF] patching ${clang_toolchain_src} ..." sed -i.orig \ -e 's,\(basePath = "/\)usr\(/lib/gcc/"\),\1opt\2,g' \ "${clang_toolchain_src}" } echo "[INF] using patch:" diff -u "${clang_toolchain_src}"{.orig,} || true echo "[INF] end of patch (${clang_toolchain_src})" fi echo "[INF] generating build system for: ${projects}" | tee "${log}" echo "${cmd[@]}" | tee -a "${log}" "${cmd[@]}" 2>&1 | tee -a "${log}" local -i retval=${?} echo local logprfx="INF" if [ ${retval} -ne 0 ] || grep -q "Configuring incomplete" "${log}" ; then logprfx="ERR" echo "[${logprfx}] failed to generate build system for: ${projects}" [ ${retval} -ne 0 ] || retval=10 else echo "[${logprfx}] successfully generated build system for: ${projects}" fi echo "[${logprfx}] build dir: '${build_dir}'" echo "[${logprfx}] cmake log: '${log}'" return ${retval} } # shellcheck disable=SC2120 ossbuild-build-llvm() { echo "[INF] ${FUNCNAME[0]} ..." local build_type="${1:-Release}" local build_args="${2:-}" local build_dir="${3:-build-${build_type,,}}" local cmd=( cmake --build "${build_dir}" -- ) if [ -n "${build_args}" ]; then read -a extra_args <<< "${build_args}" cmd+=( "${extra_args[@]}" ) elif is-qnap-qts; then cmd+=( # compiling on QTS sometimes fails (probably due to memory # limitations), so limit the parallel jobs to 2 -j2 # don't stop at failures -k0 ) fi # enable verbose logging when V is non-empty [ -z "${V:-}" ] || cmd+=(-v) echo "${cmd[@]}" "${cmd[@]}" } ossbuild-install-llvm() { echo "[INF] ${FUNCNAME[0]} ..." local build_type="${1:-Release}" local build_dir="${2:-build-${build_type,,}}" local cmd=( sudo cmake --build "${build_dir}" -- install ) echo "${cmd[@]}" "${cmd[@]}" } ossbuild-generate-llvm-fallback() { echo "[INF] ${FUNCNAME[0]} ..." local build_type="${1:-Release}" local build_dir="${2:-build-${build_type,,}}" # Fallback 1: Build a smaller subset of LLVM projects local llvm_projects=( clang clang-tools-extra lld ) local IFS=';' ossbuild-generate-llvm "${build_type}" "${llvm_projects[*]}" # Fallback 2: Generate build systems for individual projects IFS=';' read -a LLVM_PROJECTS \ <<< $(grep 'set(LLVM_ALL_PROJECTS ' llvm/CMakeLists.txt | cut -d\" -f2) local project for project in "${llvm_projects[@]}"; do ossbuild-generate-llvm "${build_type}" "${project}" done } ossbuild-llvm() { local version="${1:-15.0.5}" local build_type="${2:-Release}" # or 'RelWithDebInfo', but beware of the huge binaries ossbuild-prepare-llvm "${version}" ossbuild-generate-llvm "${build_type}" # Ideally, llcm-cmake-build should build the most LLVM projects. However, if # it doesn't, it might make sense to build projects individually (see # `ossbuild-generate-llvm-fallback` above). ossbuild-build-llvm "${build_type}" # Install the toolchain ossbuild-install-llvm "${build_type}" return $? } # stop here when sourced return 0 2>/dev/null || true set -euo pipefail [ "${DEBUG:-0}" -le 0 ] || set -x ossbuild-llvm "${@}"