Index: tools/build/cross-build-macos.sh =================================================================== --- /dev/null +++ tools/build/cross-build-macos.sh @@ -0,0 +1,357 @@ +#!/bin/sh +# Copyright (c) Mar 2025 Wolfram Schneider +# SPDX-License-Identifier: BSD-2-Clause +# +# cross-build-macos.sh - build FreeBSD/arm64 from source on macOS +# for macOS 14.x and later. Supported FreeBSD branches are +# stable/15 or later and main. +# +# Note: you need to install a new compiler and linker +# with Homebrew first: +# brew install llvm lld +# +# or with a specific clang version as LLVM20: +# brew install llvm@20 lld@20 +# +# Homebrew will install the Command Line Tools for Xcode as well. +# If not, you can install them with: xcode-select --install +# +# get the FreeBSD source: +# git clone https://codeberg.org/freebsd/freebsd-src +# cd freebsd-src && ./tools/build/cross-build-macos.sh +# +# DEV examples: +# fresh build (obj cleanup), without OPENSSL compiled, on 9 CPUs, +# make in silend mode, for the targets buildworld + buildkernel + packages + ftp tarballs +# +# env ncpu=9 build_clean=YES build_targets_opt="-s MK_OPENSSL=no" debug=1 TAR_XZ_CMD=tar PKG_FORMAT=tgz \ +# build_targets="buildworld; buildkernel;packages;-C./release obj;-C./release ftp" ./tools/build/cross-build-macos.sh +# + +set -e + +: ${debug:="1"} + +# homebrew LLVM version, e.g. for version 19: "@19" +: ${homebrew_llvm_version="@20"} + +if [ "$(uname -m)" = "arm64" ]; then + # arm64 (Apple M1 ...) + : ${homebrew_home="/opt/homebrew"} + : ${TARGET:="arm64"} + : ${TARGET_ARCH:="aarch64"} +else + # x86_64 (AMD64) + : ${homebrew_home="/usr/local"} + : ${TARGET:="amd64"} + : ${TARGET_ARCH:="amd64"} +fi + +HOMEBREW_PATH="$homebrew_home/opt/llvm${homebrew_llvm_version}/bin:$homebrew_home/opt/lld${homebrew_llvm_version}/bin:$homebrew_home/bin" +PATH="$HOMEBREW_PATH:/usr/local/bin:/usr/local/sbin:/bin:/usr/bin:/sbin:/usr/sbin"; export PATH +clt_sdk_dir="/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" + +# the cross build directories as "bmake-build" need a seperate sub-directory for multiple +# builds on the same machine +_cwd_string=$(pwd | sed -e 's,/,,' -e 's,/,-,g') + +: ${TMPDIR:="/tmp"} +: ${MAKEOBJDIRPREFIX:="$TMPDIR/freebsd-obj/${_cwd_string}"}; export MAKEOBJDIRPREFIX +: ${nice_cmd="nice -n10 time"} + +# A list of targets to build. +# If a target requires additional parameters, use a semicolon (";") for spaces +# e.g. "-C release obj; -C release ftp" +# or "-C usr.bin/locate" +# + +# everything up to ftp tarballs +: ${build_targets_release="buildworld;buildkernel;packages;-C./release obj;-C./release ftp"} + +if [ "$1" = "full" ]; then + build_targets="$build_targets_release" + shift +elif [ "$1" = "buildworld" -o "$1" = "buildkernel" -o "$1" = "packages" ]; then + : ${build_targets="$1"} + shift +elif [ "$1" = "release-ftp" ]; then + : ${build_targets="-C./release obj;-C./release ftp"} + shift +else + : ${build_targets="buildworld;buildkernel"} +fi + +# without clang TOOLCHAIN & debug: NO | YES +: ${build_fast="YES"} + +# other make flags, e.g. "-s" or makefile variable as WITHOUT_MITKRB5=1 +if [ $build_fast = "YES" ]; then + : ${build_targets_opt="__MAKE_CONF=/dev/null WITHOUT_TOOLCHAIN=yes WITHOUT_DEBUG_FILES=yes"} + + # due some bugs in bsdtar/xz and share/mk tarball can be really slow + : ${TAR_XZ_CMD="tar -Z"} + : ${PKG_FORMAT="tgz"} + export TAR_XZ_CMD PKG_FORMAT +else + : ${build_targets_opt=""} +fi + +# cleanup objdir for a fresh build (aka `make distclean') +: ${build_clean="NO"} + +# by default show disk usage only for buildworld +case $build_targets in + *buildworld* ) : ${build_show_du="YES"} ;; + * ) : ${build_show_du="NO"} ;; +esac + +: ${ncpu:=$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 1)} +: ${logrotate_max="32"} + +if [ "$1" = "help" ]; then + +cat <&2 + exit 1 +fi + +if [ -z "$build_targets" ]; then + echo "env variable 'build_targets' is empty, nothing to do - give up" >&2 + exit 1 +fi + +for i in clang clang++ clang-cpp ld.lld python3 +do + if ! env PATH=$HOMEBREW_PATH /usr/bin/which $i >/dev/null; then + echo "missing program: $i for PATH=$HOMEBREW_PATH" >&2 + exit 1 + fi +done + +# https://wiki.freebsd.org/ExternalToolchain +export CPP="clang-cpp" +export CC="clang" +export CXX="clang++" +export XCC="clang" + +# due a bug in share/mk the XLD/LD value must be a full path +#export XLD="ld.lld" +export XLD="$homebrew_home/opt/lld${homebrew_llvm_version}/bin/ld.lld" +export XCPP="clang-cpp" +export XCXX="clang++" + +logrotate () +{ + local file="$1" + + if [ "$logrotate_max" = "" ]; then + return + elif [ "$logrotate_max" -le 0 ]; then + return + fi + + for number in $(seq $logrotate_max 0) + do + next=$(($number + 1)) + if [ -e "$file.$number.gz" -a $next -lt $logrotate_max ]; then + mv -f "$file.$number.gz" "$file.$next.gz" + fi + done + if [ -e "$file" ]; then + mv "$file" "$file.0" + gzip -f "$file.0" + fi +} + +decho() { + local message="$1" + # -1 | 0 | 1 - use -1 to always show error messages + local d="$2" + # log to stdout and the logfile + local stdout="$3" + + if [ -z "$d" ]; then + d="0" + fi + + # debug messages to stderr + if [ $debug -gt $d ]; then + echo "-- $message" >&2 + fi + + # debug messages to logfile + if [ -n "$stdout" ]; then + echo "$message" + fi +} + +: ${build_clean_delay=4} +if [ "$build_clean" = "YES" ]; then + decho "cleanup obj files in ${build_clean_delay} seconds" + decho "MAKEOBJDIRPREFIX=$MAKEOBJDIRPREFIX" + + # last change to hit ^C + for i in $(seq 1 ${build_clean_delay}) + do + printf "."; sleep 1 + done + echo "" + + rm -rf $MAKEOBJDIRPREFIX +else + decho "set MAKEOBJDIRPREFIX=$MAKEOBJDIRPREFIX" +fi +mkdir -p $MAKEOBJDIRPREFIX + +logrotate "macos.setup" +# log infos about macOS setup +( +echo "Start time: $(date)" +sysctl machdep.cpu.brand_string +sysctl kern.osproductversion +echo "$(sysctl hw.physicalcpu) / P-cores: $(sysctl hw.perflevel0.physicalcpu) / E-cores: $(sysctl hw.perflevel1.physicalcpu)" +sysctl hw.memsize | awk '{ printf("%s %2.1f GB\n", $1, $2/1024/1024/1024) }' + +echo "Xcode path: $(xcode-select -p)" +echo "Xcode version: $(pkgutil --pkg-info=com.apple.pkg.CLTools_Executables | grep '^version: ')" + +if [ -d $clt_sdk_dir ]; then + echo "SDK version: $(ls -l $clt_sdk_dir)" +fi + +if [ -d .git ]; then + decho "local git branch: $(git branch | grep '^\*' | sed -e 's/^\* //')" 0 stdout + decho "last git commit: $(git log --oneline | head -n1)" 0 stdout +fi + +# program version +cat <&1) +EOF + +# env variables for cross compiling +cat < macos.setup + +# +# build FreeBSD world and kernel +# +# expected runtime without TOOLCHAIN (build_fast="YES") for +# iMac (Intel Core i5, 2019, 6-cores) ca. 119min +# MacBook Air (M1, 2020, 4P/4E cores) ca. 42min +# MacBook Pro (M1, 2021, 8P/2E cores) ca. 21min +# +# with TOOLCHAIN (build_fast="NO") it will be 2x slower +# +( +set -e + +OLD_IFS=$IFS +IFS=";" + +for target in $build_targets +do + IFS=$OLD_IFS + log=$(echo $target | sed -E -e 's/[ ]?\-[^ ]+[ ]//; s/ /-/g; s/\//-/g') + + # keep logs from previous run + logrotate "macos.$log" + + decho "start target '$target' at $(date)" 0 x >> macos.setup + + # cross compiling + if $nice_cmd ./tools/build/make.py -j$ncpu TARGET=$TARGET TARGET_ARCH=$TARGET_ARCH $build_targets_opt $target > macos.$log 2>&1; then + : + else + exit=$? + + # keep track of bad git commits + git log --oneline | head -n1 > ".cross-build-macos-git-bad" + + decho "make target '$target' failed with exit status: $exit - see the log file ./macos.$log" -1 x >> macos.setup + exit $exit + fi +done +) + +( + if [ "$build_show_du" = "YES" ]; then + echo "" + echo "MAKEOBJDIRPREFIX disk usage:" + du -hs $MAKEOBJDIRPREFIX + fi + + # keep track of good git commits + git log --oneline | head -n1 > ".cross-build-macos-git-good" + + echo "" + decho ">>> Build of target(s) $build_targets were successfully executed at $(date) <<<" 0 stdout +) >> macos.setup + +#EOF