584 lines
16 KiB
Bash
584 lines
16 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
$0 [-u|--usage|-h|--help]
|
|
[--list] [-n|--dry-run] [--xoname=NAME]
|
|
[--repo
|
|
|--add-umbrella-remote
|
|
|--split-umbrella-remote
|
|
|--pull-umbrella-remote
|
|
|--push-umbrella-remote]
|
|
[--clone|--configure|--build|--install]
|
|
[--with-examples] [--with-utests] [--with-asm] [--debug-build]
|
|
[--with-opengl=GLFLAG] [--with-vulkan=VKFLAG]
|
|
[-S=SOURCEDIR] [-B=BUILDDIR] [-j N|--jobs=N]
|
|
[--all | [--with-deps] NAME ...]
|
|
EOF
|
|
}
|
|
|
|
help() {
|
|
usage
|
|
|
|
cat <<EOF
|
|
|
|
fetch and/or build xo component libraries
|
|
|
|
Options:
|
|
-u | --usage brief help message
|
|
-h | --help this help message
|
|
--list list known xo libraries
|
|
-n | --dry-run dry-run: don't actually invoke state-changing commands
|
|
--xoname=NAME operate on xo subsystem NAME
|
|
--all operate on every subsystem from --list, in dependency
|
|
order (bottom-up), excluding the xo-cmake bootstrap.
|
|
The chosen operations (e.g. --configure --build --utest
|
|
--install) run for each subsystem before moving to the
|
|
next, so a whole-umbrella cmake build is one command.
|
|
Multiple NAMEs may also be given explicitly instead.
|
|
--with-deps expand the named subsystem(s) to their transitive
|
|
dependency closure (using subsystem-edges) and operate
|
|
on the whole closure in bottom-up order. Use this to
|
|
build a subsystem from scratch (e.g. in a fresh docker
|
|
container) where its dependencies are not yet installed.
|
|
Ignored with --all.
|
|
--clone git clone xo library NAME in current directory
|
|
--configure run cmake for xo library NAME in immediate subdir
|
|
Will use NAME/.build as build directory
|
|
--build run cmake build for xo library NAME in immediate subdir
|
|
--build-docs build documentation for xo library NAME in immediate subdir
|
|
--utest run unit tests for xo library NAME
|
|
--install run cmake install for xo library NAME in immediate subdir
|
|
--realclean nuke build directory
|
|
|
|
-S=SOURCEDIR override path/to/source
|
|
-B=BUILDDIR override path/to/build
|
|
-j N | --jobs=N build with N parallel compile jobs (cmake --build -j N).
|
|
Default: min(nproc, RAM/~1.5GB), to avoid OOM-killing
|
|
the compiler on memory-limited machines. Takes
|
|
precedence over the XO_BUILD_JOBS environment variable.
|
|
|
|
--with-examples in configure step, set -DXO_ENABLE_EXAMPLES=1
|
|
--with-utests in configure step, set -DENABLE_TESTING=1
|
|
--with-asm in configure step, set -DXO_ENABLE_ASM=1
|
|
--debug-build in configure step, set -DCMAKE_BUILD_TYPE=Debug
|
|
--with-opengl=GLFLAG in configure step, set -DXO_ENABLE_OPENGL=GLFLAG [ON]
|
|
--with-vulkan=VKFLAG in configure step, set -DXO_ENABLE_VULKAN=VKFLAG [OFF]
|
|
|
|
--repo report github repo for NAME
|
|
|
|
--add-umbrella-remote (in umbrella sandbox) add remote for xo library NAME
|
|
--split-umbrella-remote (in umbrella sandbox) generate split branch for xo library NAME
|
|
--pull-umbrella-remote (in umbrella sandbox) pull satellite remote for xo library NAME
|
|
--push-umbrella-remote (in umbrella sandbox) push satellite remote for xo library NAME
|
|
|
|
EOF
|
|
}
|
|
|
|
cmd=
|
|
noop_flag=0
|
|
xoname=
|
|
clone_flag=0
|
|
configure_flag=0
|
|
build_flag=0
|
|
build_docs_flag=0
|
|
utest_flag=0
|
|
install_flag=0
|
|
realclean_flag=0
|
|
pathtosource=
|
|
pathtobuild=
|
|
jobs_arg=
|
|
with_examples=0
|
|
with_utests=0
|
|
with_asm=0
|
|
debug_build=0
|
|
with_opengl=
|
|
with_vulkan=
|
|
repo_flag=0
|
|
add_umbrella_remote_flag=0
|
|
split_umbrella_remote_flag=0
|
|
pull_umbrella_remote_flag=0
|
|
push_umbrella_remote_flag=0
|
|
all_flag=0
|
|
with_deps_flag=0
|
|
xonames=()
|
|
|
|
while [[ $# > 0 ]]; do
|
|
case "$1" in
|
|
-u | --usage)
|
|
cmd='usage'
|
|
;;
|
|
-h | --help)
|
|
cmd='help'
|
|
;;
|
|
-n | --dry-run)
|
|
noop_flag=1
|
|
;;
|
|
-S)
|
|
shift
|
|
pathtosource=$1
|
|
;;
|
|
-S=*)
|
|
pathtosource="${1#*=}"
|
|
;;
|
|
-B)
|
|
shift
|
|
pathtobuild=$1
|
|
;;
|
|
-B=*)
|
|
pathtobuild="${1#*=}"
|
|
;;
|
|
-j)
|
|
shift
|
|
jobs_arg=$1
|
|
;;
|
|
-j=*)
|
|
jobs_arg="${1#*=}"
|
|
;;
|
|
--jobs)
|
|
shift
|
|
jobs_arg=$1
|
|
;;
|
|
--jobs=*)
|
|
jobs_arg="${1#*=}"
|
|
;;
|
|
--list)
|
|
cmd='list'
|
|
;;
|
|
--all)
|
|
all_flag=1
|
|
;;
|
|
--with-deps)
|
|
with_deps_flag=1
|
|
;;
|
|
--xoname=*)
|
|
xoname="${1#*=}"
|
|
xonames+=("${1#*=}")
|
|
;;
|
|
--clone)
|
|
clone_flag=1
|
|
;;
|
|
--configure|--config)
|
|
configure_flag=1
|
|
;;
|
|
--build)
|
|
build_flag=1
|
|
;;
|
|
--build-docs)
|
|
build_docs_flag=1
|
|
;;
|
|
--utest)
|
|
utest_flag=1
|
|
;;
|
|
--install)
|
|
install_flag=1
|
|
;;
|
|
--realclean)
|
|
realclean_flag=1
|
|
;;
|
|
--with-examples)
|
|
with_examples=1
|
|
;;
|
|
--with-utests)
|
|
with_utests=1
|
|
;;
|
|
--with-asm)
|
|
with_asm=1
|
|
;;
|
|
--debug-build)
|
|
debug_build=1
|
|
;;
|
|
--with-opengl=*)
|
|
with_opengl="${1#*=}"
|
|
;;
|
|
--with-vulkan=*)
|
|
with_vulkan="${1#*=}"
|
|
;;
|
|
--repo)
|
|
repo_flag=1
|
|
;;
|
|
--add-umbrella-remote)
|
|
add_umbrella_remote_flag=1
|
|
;;
|
|
--split-umbrella-remote)
|
|
split_umbrella_remote_flag=1
|
|
;;
|
|
--pull-umbrella-remote)
|
|
pull_umbrella_remote_flag=1
|
|
;;
|
|
--push-umbrella-remote)
|
|
push_umbrella_remote_flag=1
|
|
;;
|
|
xo-*)
|
|
xoname="$1"
|
|
xonames+=("$1")
|
|
;;
|
|
*)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
shift
|
|
done
|
|
|
|
#echo xoname=$xoname pathtosource=$pathtosource pathtobuild=$pathtobuild
|
|
|
|
# -S/-B overrides (if given) apply per-subsystem below; when empty the
|
|
# source/build dirs are derived from each subsystem name in the loop.
|
|
orig_pathtosource="$pathtosource"
|
|
orig_pathtobuild="$pathtobuild"
|
|
|
|
# compile parallelism (jobs passed to cmake --build -j). Precedence:
|
|
# 1. -j N / --jobs=N command-line argument
|
|
# 2. XO_BUILD_JOBS environment variable
|
|
# 3. memory-aware default (below)
|
|
#
|
|
# A bare "cmake --build -j" passes a no-count -j to make, which runs UNLIMITED
|
|
# parallel jobs. On a template-heavy subsystem (e.g. xo-expression2's ~49 facet
|
|
# translation units, ~300MB each) that OOM-kills cc1plus on a memory-limited
|
|
# runner (symptom: "g++: fatal error: Killed signal terminated program cc1plus").
|
|
# So the default caps the job count to what RAM allows (~1.5GB budget per job),
|
|
# bounded by cpu count.
|
|
if [[ -n "${jobs_arg}" ]]; then
|
|
build_jobs="${jobs_arg}"
|
|
elif [[ -n "${XO_BUILD_JOBS:-}" ]]; then
|
|
build_jobs="${XO_BUILD_JOBS}"
|
|
else
|
|
build_ncpu=$(nproc 2>/dev/null || echo 1)
|
|
build_memkb=$(awk '/^MemTotal:/ { print $2 }' /proc/meminfo 2>/dev/null || echo 0)
|
|
if [[ "${build_memkb}" -gt 0 ]]; then
|
|
build_memjobs=$(( build_memkb / 1500000 )) # ~1.5GB per compile job
|
|
[[ "${build_memjobs}" -lt 1 ]] && build_memjobs=1
|
|
if [[ "${build_memjobs}" -lt "${build_ncpu}" ]]; then
|
|
build_jobs="${build_memjobs}"
|
|
else
|
|
build_jobs="${build_ncpu}"
|
|
fi
|
|
else
|
|
build_jobs="${build_ncpu}"
|
|
fi
|
|
fi
|
|
|
|
SUBSYSTEMLIST_FILE=@CMAKE_INSTALL_FULL_DATADIR@/etc/xo/subsystem-list
|
|
SUBSYSTEMEDGES_FILE=@CMAKE_INSTALL_FULL_DATADIR@/etc/xo/subsystem-edges
|
|
|
|
subsystem_list() {
|
|
cat ${SUBSYSTEMLIST_FILE}
|
|
}
|
|
|
|
XO_REPO_STEM=
|
|
if [[ $(whoami) == "roland" ]]; then
|
|
XO_REPO_STEM="git@github.com:Rconybea"
|
|
else
|
|
XO_REPO_STEM="https://github.com/Rconybea"
|
|
fi
|
|
|
|
printrepo() {
|
|
xoname=$1
|
|
|
|
if grep -q $1 ${SUBSYSTEMLIST_FILE}; then
|
|
echo "${XO_REPO_STEM}/${1}.git"
|
|
else
|
|
>&2 echo "$0: unknown xo component [${xoname}]"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
if [[ $cmd == 'usage' ]]; then
|
|
echo -n "usage: "
|
|
usage
|
|
exit 0
|
|
elif [[ $cmd == 'help' ]]; then
|
|
echo -n "help; "
|
|
help
|
|
exit 0
|
|
fi
|
|
|
|
set -e
|
|
|
|
if [[ $cmd == 'list' ]]; then
|
|
subsystem_list
|
|
fi
|
|
|
|
# resolve the set of subsystems to operate on.
|
|
# --all -> every subsystem in dependency order (bottom-up),
|
|
# excluding the xo-cmake bootstrap itself
|
|
# one/more NAMEs -> exactly those, in the order given
|
|
if [[ $all_flag -eq 1 ]]; then
|
|
mapfile -t xonames < <(subsystem_list | grep -v '^xo-cmake$')
|
|
fi
|
|
|
|
# --with-deps: expand the named subsystems to their transitive dependency
|
|
# closure (via subsystem-edges), then order the closure bottom-up using
|
|
# subsystem-list (itself a valid topological order). No effect together with
|
|
# --all (already the whole graph) or when no subsystem is named.
|
|
if [[ $with_deps_flag -eq 1 && $all_flag -eq 0 && ${#xonames[@]} -gt 0 ]]; then
|
|
if [[ ! -r "${SUBSYSTEMEDGES_FILE}" ]]; then
|
|
>&2 echo "$0: --with-deps: cannot read edges file ${SUBSYSTEMEDGES_FILE}"
|
|
exit 1
|
|
fi
|
|
|
|
# deps_of[dependent] = space-separated direct dependencies.
|
|
# each edge line is "<dep> <dependent>".
|
|
declare -A deps_of=()
|
|
while read -r edge_dep edge_dependent; do
|
|
[[ -z "$edge_dep" || -z "$edge_dependent" ]] && continue
|
|
deps_of["$edge_dependent"]+=" $edge_dep"
|
|
done < "${SUBSYSTEMEDGES_FILE}"
|
|
|
|
# transitive closure of the requested subsystems (worklist to fixpoint)
|
|
declare -A in_closure=()
|
|
worklist=("${xonames[@]}")
|
|
while [[ ${#worklist[@]} -gt 0 ]]; do
|
|
cur="${worklist[0]}"
|
|
worklist=("${worklist[@]:1}")
|
|
[[ -n "${in_closure[$cur]:-}" ]] && continue
|
|
in_closure["$cur"]=1
|
|
for d in ${deps_of[$cur]:-}; do
|
|
[[ -z "${in_closure[$d]:-}" ]] && worklist+=("$d")
|
|
done
|
|
done
|
|
|
|
# rebuild xonames in bottom-up (subsystem-list) order, closure members only
|
|
xonames=()
|
|
while IFS= read -r sub; do
|
|
[[ -n "${in_closure[$sub]:-}" ]] && xonames+=("$sub")
|
|
done < <(subsystem_list)
|
|
fi
|
|
|
|
# preserve prior single-shot no-op behavior when no subsystem is named
|
|
if [[ ${#xonames[@]} -eq 0 ]]; then
|
|
xonames=("")
|
|
fi
|
|
|
|
# --- per-subsystem operations (loop body is intentionally left un-indented) ---
|
|
for xoname in "${xonames[@]}"; do
|
|
|
|
if [[ -n "$orig_pathtosource" ]]; then
|
|
pathtosource="$orig_pathtosource"
|
|
else
|
|
pathtosource="$xoname"
|
|
fi
|
|
|
|
if [[ -n "$orig_pathtobuild" ]]; then
|
|
pathtobuild="$orig_pathtobuild"
|
|
elif [[ -n "$xoname" ]]; then
|
|
pathtobuild="$xoname/.build"
|
|
else
|
|
pathtobuild=".build"
|
|
fi
|
|
|
|
if [[ $repo_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
printrepo $xoname
|
|
fi
|
|
fi
|
|
|
|
if [[ $add_umbrella_remote_flag -eq 1 ]]; then
|
|
if [[ $clone_flag -eq 1 || $configure_flag -eq 1 || $build_flag -eq 1 || $build_docs_flag -eq 1 || $install_flag -eq 1 || $realclean_flag -eq 1 ]]; then
|
|
2>&1 echo "error: xo-build: repo operation --add-umbrella-remote conflicts with"
|
|
2>&1 echo " build operation --clone|--configure|--build|--build-docs|--install|--realclean"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "$xoname" ]]; then
|
|
url=$(printrepo $xoname)
|
|
|
|
cmd="git remote add $xoname $url"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
$cmd
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $split_umbrella_remote_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
#branch=$(git branch --show-current)
|
|
demux_branch=_demux/$xoname
|
|
cmd="git subtree split --rejoin --prefix=$xoname -b ${demux_branch}"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
$cmd
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $pull_umbrella_remote_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
branch=$(git branch --show-current)
|
|
cmd="git subtree pull --rejoin --prefix=$xoname $xoname ${branch}"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
$cmd
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $push_umbrella_remote_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
branch=$(git branch --show-current)
|
|
demux_branch=_demux/$xoname
|
|
cmd1="git subtree split --rejoin --prefix=$xoname -b ${demux_branch}"
|
|
remote=${xoname}
|
|
remote_branch=${branch}
|
|
cmd2="git push ${remote} ${demux_branch}:${remote_branch}"
|
|
#cmd="git subtree push --rejoin --prefix=$xoname $xoname ${branch}"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd1
|
|
echo $cmd2
|
|
else
|
|
$cmd1
|
|
$cmd2
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $clone_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
url=$(printrepo $xoname)
|
|
|
|
cmd="git clone $url $xoname"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
$cmd
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $configure_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
testingarg=
|
|
if [[ $with_utests -eq 1 ]]; then
|
|
testingarg="-DENABLE_TESTING=1"
|
|
fi
|
|
|
|
asmarg=
|
|
if [[ $with_asm -eq 1 ]]; then
|
|
asmarg="-DENABLE_ASM=1"
|
|
fi
|
|
|
|
examplearg=
|
|
if [[ $with_examples -eq 1 ]]; then
|
|
examplearg="-DXO_ENABLE_EXAMPLES=1"
|
|
fi
|
|
|
|
cmakebuildtype=
|
|
if [[ $debug_build -eq 1 ]]; then
|
|
cmakebuildtype="-DCMAKE_BUILD_TYPE=Debug"
|
|
fi
|
|
|
|
openglarg=
|
|
if [[ -n ${with_opengl} ]]; then
|
|
openglarg="-DXO_ENABLE_OPENGL=${with_opengl}"
|
|
fi
|
|
|
|
vulkanarg=
|
|
if [[ -n ${with_vulkan} ]]; then
|
|
vulkanarg="-DXO_ENABLE_VULKAN=${with_vulkan}"
|
|
fi
|
|
|
|
cmd="cmake -DCMAKE_INSTALL_PREFIX=@CMAKE_INSTALL_PREFIX@ -DCMAKE_MODULE_PATH=@CMAKE_INSTALL_PREFIX@/share/cmake -S $pathtosource -B $pathtobuild ${testingarg} ${examplearg} ${asmarg} ${openglarg} ${vulkanarg} ${cmakebuildtype}"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
cwd=$(pwd)
|
|
# write script to re-run cmake later
|
|
rebootcmake=$pathtobuild/rebootcmake
|
|
|
|
mkdir -p $(dirname ${rebootcmake})
|
|
|
|
cat > ${rebootcmake} <<EOF
|
|
#!/usr/bin/env bash
|
|
|
|
cd ${cwd} && ${cmd}
|
|
|
|
EOF
|
|
chmod +rx ${rebootcmake}
|
|
|
|
$cmd
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $build_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
cmd="cmake --build $pathtobuild -j$build_jobs"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
$cmd
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $build_docs_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
cmd="cmake --build $pathtobuild -- docs"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
$cmd
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $utest_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
cmd="ctest --test-dir $pathtobuild"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
# On failure, re-run just the failed tests verbosely so CI logs show
|
|
# WHY they failed -- the first pass reports only pass/fail per test,
|
|
# whereas --output-on-failure prints each failed test's output (e.g.
|
|
# the Catch2 assertion detail). Preserve the failing exit status so
|
|
# the build still fails.
|
|
if ! $cmd; then
|
|
echo "xo-build: unit tests failed -- re-running failures with --rerun-failed --output-on-failure" 1>&2
|
|
ctest --test-dir "$pathtobuild" --rerun-failed --output-on-failure || true
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $install_flag -eq 1 ]]; then
|
|
if [[ -n "$xoname" ]]; then
|
|
cmd="cmake --install $pathtobuild"
|
|
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo $cmd
|
|
else
|
|
$cmd
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ $realclean_flag -eq 1 ]]; then
|
|
if [[ $noop_flag -eq 1 ]]; then
|
|
echo "rm -rf $pathtobuild"
|
|
else
|
|
rm -rf "${pathtobuild}"
|
|
fi
|
|
fi
|
|
|
|
done
|