-
-
Save tueda/d802b76760451a571d53a974db20d096 to your computer and use it in GitHub Desktop.
Running a development version of FORM. #bin #sh #launcher #dev #form
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Usage: | |
# ./x [update|clean] | |
# [4.1.0|4.2.0|4.2.1|4.3.0|4.3.1] | |
# [check|time [-n <times>]] | |
# [coverage|gprof|valgrind [<valgrind options>]|helgrind|drd|sanitize] | |
# [form|vorm|tform|tvorm|parform|parvorm] | |
# [-np <nprocs>] | |
# [args...] | |
# | |
# valgrind-options = | |
# [--leak-check=<no|summary|yes|full>] | |
# [--show-leak-kinds=<set>] | |
# | |
set -eu | |
set -o pipefail | |
TAG= | |
VERSION= | |
BUILD_DIR=build | |
TARGET=default | |
CONFIG_OPTS= | |
MAKE_OPTS= | |
# The default tools. | |
CC=gcc | |
CXX=g++ | |
MPICC= | |
MPICXX= | |
MPIRUN=mpirun | |
GCOV=gcov | |
GMP_DIR= | |
MPFR_DIR= | |
ZLIB_DIR= | |
VALGRIND=valgrind | |
LCOV=lcov | |
GENHTML=genhtml | |
GPROF=gprof | |
GPROF2DOT=gprof2dot | |
CURL=curl | |
NPROC=nproc | |
TIME=/usr/bin/time # GNU Time | |
if command -v brew >/dev/null; then | |
# We assume that they are installed by Homebrew on Linux, i.e., | |
# brew install gcc@14 gmp gprof2dot lcov mpfr valgrind | |
# yes | perl -MCPAN -e 'install Date::Parse' | |
# yes | perl -MCPAN -e 'install JSON::XS' # optional | |
GCC_VERSION=14 | |
GCC_SUFFIX=-$GCC_VERSION | |
CC=$(brew --prefix gcc@$GCC_VERSION)/bin/gcc$GCC_SUFFIX | |
CXX=$(brew --prefix gcc@$GCC_VERSION)/bin/g++$GCC_SUFFIX | |
GCOV=$(brew --prefix gcc@$GCC_VERSION)/bin/gcov$GCC_SUFFIX | |
GMP_DIR=$(brew --prefix gmp) | |
MPFR_DIR=$(brew --prefix mpfr) | |
VALGRIND=$(brew --prefix valgrind)/bin/valgrind | |
LCOV=$(brew --prefix lcov)/bin/lcov | |
GENHTML=$(brew --prefix lcov)/bin/genhtml | |
GPROF=$(brew --prefix binutils)/bin/gprof | |
GPROF2DOT=$(brew --prefix gprof2dot)/bin/gprof2dot | |
DOT=$(brew --prefix graphviz)/bin/dot | |
CURL=$(brew --prefix curl)/bin/curl | |
fi | |
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) | |
SCRIPT_FILE="$SCRIPT_DIR/$(basename -- "${BASH_SOURCE[0]}")" | |
if [ -f "$SCRIPT_DIR/x.local" ]; then | |
# shellcheck disable=SC1091 | |
. "$SCRIPT_DIR/x.local" # local settings | |
fi | |
CONFIG_OPTS="$CONFIG_OPTS CC=$CC CXX=$CXX --enable-debug" | |
if [ -n "$GMP_DIR" ]; then | |
CONFIG_OPTS="$CONFIG_OPTS --with-gmp=$GMP_DIR" | |
fi | |
if [ -n "$MPFR_DIR" ]; then | |
CONFIG_OPTS="$CONFIG_OPTS --with-mpfr=$MPFR_DIR" | |
fi | |
if [ -n "$ZLIB_DIR" ]; then | |
CONFIG_OPTS="$CONFIG_OPTS --with-zlib=$ZLIB_DIR" | |
fi | |
VALGRIND_OPTS= | |
MPIRUN_OPTS= | |
DO_CLEAN=false | |
DO_CHECK=false | |
DO_TIME=false | |
DO_COVERAGE=false | |
DO_GPROF=false | |
DO_VALGRIND=false | |
DO_HELGRIND=false | |
DO_DRD=false | |
DO_SANITIZE=false | |
N=default | |
while (( $# > 0 )); do | |
case "$1" in | |
form|tform|vorm|tvorm|parform|parvorm) | |
TARGET="$1" | |
shift | |
;; | |
check) | |
DO_CHECK=: | |
shift | |
;; | |
time) | |
DO_TIME=: | |
shift | |
;; | |
-n) | |
shift | |
if (( $# == 0 )); then | |
echo "error: -n option requires a non-zero integer argument" >&2 | |
exit 1 | |
fi | |
N=$1 | |
shift | |
;; | |
-np) | |
shift | |
if (( $# == 0 )); then | |
echo "error: -np option requires a non-zero integer argument" >&2 | |
exit 1 | |
fi | |
MPIRUN_OPTS="$MPIRUN_OPTS -np $1" | |
shift | |
;; | |
cov|coverage) | |
DO_COVERAGE=: | |
shift | |
;; | |
prof|gprof) | |
DO_GPROF=: | |
shift | |
;; | |
valgrind) | |
DO_VALGRIND=: | |
shift | |
;; | |
--leak-check=*|--show-leak-kinds=*) | |
VALGRIND_OPTS="$VALGRIND_OPTS $1" | |
shift | |
;; | |
helgrind) | |
DO_HELGRIND=: | |
shift | |
;; | |
drd) | |
DO_DRD=: | |
shift | |
;; | |
sanitize) | |
DO_SANITIZE=: | |
shift | |
;; | |
4.2.0|4.2.1|4.3.0|4.3.1) | |
TAG=v$1 | |
VERSION=$1 | |
shift | |
;; | |
4.1.0) | |
TAG=v4.1-20131025 | |
VERSION=4.1.0 | |
MAKE_OPTS="DATE='Oct 25 2013'" | |
shift | |
;; | |
# 4.0.1) | |
# TAG=v4.0-20120410 | |
# VERSION=4.0.1 | |
# MAKE_OPTS="DATE='Apr 10 2012'" | |
# shift | |
# ;; | |
# 4.0.0) | |
# TAG=v4.0-20120330 | |
# VERSION=4.0.0 | |
# MAKE_OPTS="DATE='Mar 29 2012'" | |
# shift | |
# ;; | |
update) | |
download_file="$SCRIPT_FILE.tmp$$" | |
"$CURL" -o "$download_file" https://gist.githubusercontent.com/tueda/d802b76760451a571d53a974db20d096/raw/x | |
if diff -q "$SCRIPT_FILE" "$download_file" >/dev/null; then | |
echo "$SCRIPT_FILE up-to-date" | |
rm -f "$download_file" | |
else | |
diff -u "$SCRIPT_FILE" "$download_file" && : | |
chmod +x "$download_file" | |
mv "$download_file" "$SCRIPT_FILE" | |
echo "$SCRIPT_FILE updated" | |
fi | |
exit | |
;; | |
clean) | |
DO_CLEAN=: | |
shift | |
;; | |
*) | |
break | |
;; | |
esac | |
done | |
check_excl_arg_begin() { | |
_check_excl_arg_n=0 | |
_check_excl_arg_s="" | |
} | |
check_excl_arg() { | |
local _check_excl_arg_tool | |
_check_excl_arg_tool="DO_$(echo "$1" | tr '[:lower:]' '[:upper:]')" | |
if ${!_check_excl_arg_tool}; then | |
((++_check_excl_arg_n)) | |
[[ -z "$_check_excl_arg_s" ]] || _check_excl_arg_s+=" and " | |
_check_excl_arg_s+="$1" | |
fi | |
} | |
check_excl_arg_end() { | |
if [ $_check_excl_arg_n -gt 1 ]; then | |
echo "error: cannot use $_check_excl_arg_s together" >&2 | |
exit 1 | |
fi | |
} | |
check_excl_arg_begin | |
check_excl_arg check | |
check_excl_arg time | |
check_excl_arg_end | |
check_excl_arg_begin | |
check_excl_arg time | |
check_excl_arg coverage | |
check_excl_arg gprof | |
check_excl_arg valgrind | |
check_excl_arg helgrind | |
check_excl_arg drd | |
check_excl_arg sanitize | |
check_excl_arg_end | |
if $DO_TIME; then | |
case $TARGET in | |
default) | |
TARGET=form | |
;; | |
esac | |
case $N in | |
default) | |
;; | |
*) | |
if [[ ! $N =~ ^[1-9][0-9]*$ ]]; then | |
echo "error: -n option requires a non-zero integer argument: $N" >&2 | |
exit 1 | |
fi | |
;; | |
esac | |
if [ $# -eq 0 ]; then | |
echo "error: nothing to measure time" >&2 | |
exit 1 | |
fi | |
else | |
case $N in | |
default) | |
;; | |
*) | |
echo "error: -n $N can be used only with time" >&2 | |
exit 1 | |
;; | |
esac | |
fi | |
if $DO_COVERAGE; then | |
case $TARGET in | |
form|tform|parform) | |
echo "error: use coverage only with debugging versions" >&2 | |
exit 1 | |
;; | |
esac | |
if ! $DO_CHECK && [ $# -eq 0 ]; then | |
echo "error: nothing to check coverage" >&2 | |
exit 1 | |
fi | |
BUILD_DIR="$BUILD_DIR-coverage" | |
CONFIG_OPTS="$CONFIG_OPTS --enable-coverage" | |
fi | |
if $DO_GPROF; then | |
case $TARGET in | |
default) | |
TARGET=form | |
;; | |
vorm|tvorm|parvorm) | |
echo "error: use gprof only with release versions" >&2 | |
exit 1 | |
;; | |
esac | |
if ! $DO_CHECK && [ $# -eq 0 ]; then | |
echo "error: nothing to perform profiling" >&2 | |
exit 1 | |
fi | |
BUILD_DIR="$BUILD_DIR-gprof" | |
CONFIG_OPTS="$CONFIG_OPTS --enable-profile=gprof" | |
fi | |
if $DO_HELGRIND; then | |
case $TARGET in | |
default) | |
TARGET=tvorm | |
;; | |
esac | |
DO_VALGRIND=: | |
DO_HELGRIND=false | |
VALGRIND_OPTS=--tool=helgrind | |
fi | |
if $DO_DRD; then | |
case $TARGET in | |
default) | |
TARGET=tvorm | |
;; | |
esac | |
DO_VALGRIND=: | |
DO_DRD=false | |
VALGRIND_OPTS=--tool=drd | |
fi | |
if $DO_SANITIZE; then | |
case $TARGET in | |
form|tform|parform) | |
echo "error: use sanitize only with debugging versions" >&2 | |
exit 1 | |
;; | |
esac | |
BUILD_DIR="$BUILD_DIR-sanitize" | |
CONFIG_OPTS="$CONFIG_OPTS --enable-sanitize" | |
fi | |
case $TARGET in | |
default) | |
TARGET=vorm | |
;; | |
parform|parvorm) | |
BUILD_DIR="$BUILD_DIR-parform" | |
CONFIG_OPTS="$CONFIG_OPTS MPICC=$MPICC MPICXX=$MPICXX --enable-parform" | |
;; | |
esac | |
run() { | |
_run_echo "$@" | |
"$@" | |
} | |
_run_echo() { | |
echo "Running $*" | |
} | |
analyze_stat() { | |
local n=$1 | |
shift | |
stat_version | |
echo "$@" | stat_command 'Command being timed' | |
stat_header "$n" | |
echo "$@" | stat_number 'User time (seconds)' | |
echo "$@" | stat_number 'System time (seconds)' | |
echo "$@" | stat_percent 'Percent of CPU this job got' | |
echo "$@" | stat_time 'Elapsed (wall clock) time (h:mm:ss or m:ss)' | |
echo "$@" | stat_number 'Average shared text size (kbytes)' | |
echo "$@" | stat_number 'Average unshared data size (kbytes)' | |
echo "$@" | stat_number 'Average stack size (kbytes)' | |
echo "$@" | stat_number 'Average total size (kbytes)' | |
echo "$@" | stat_number 'Maximum resident set size (kbytes)' | |
echo "$@" | stat_number 'Average resident set size (kbytes)' | |
echo "$@" | stat_number 'Major (requiring I/O) page faults' | |
echo "$@" | stat_number 'Minor (reclaiming a frame) page faults' | |
echo "$@" | stat_number 'Voluntary context switches' | |
echo "$@" | stat_number 'Involuntary context switches' | |
echo "$@" | stat_number 'Swaps' | |
echo "$@" | stat_number 'File system inputs' | |
echo "$@" | stat_number 'File system outputs' | |
echo "$@" | stat_number 'Socket messages sent' | |
echo "$@" | stat_number 'Socket messages received' | |
echo "$@" | stat_number 'Signals delivered' | |
echo "$@" | stat_number 'Page size (bytes)' | |
echo "$@" | stat_status 'Exit status' | |
} | |
stat_version() { | |
echo " Revision: $("$SCRIPT_DIR/scripts/git-version-gen.sh" -C "$SCRIPT_DIR" | sed -n 3p) ($(git -C "$SCRIPT_DIR" rev-parse --abbrev-ref HEAD))" | |
} | |
stat_command() { | |
printf " %s: " "$1" | |
extract_values "$1" | sed -n '1p' | |
} | |
stat_header() { | |
printf " %-40s" "n = $1" | |
printf "%14s (%12s) %14s %14s" 'Mean' 'SD' 'Min' 'Max' | |
echo "" | |
} | |
stat_number() { | |
printf " %-40s" "$1:" | |
extract_values "$1" | calc_stat | |
} | |
stat_percent() { | |
printf " %-40s" "$1 (%):" | |
extract_values "$1" | sed 's/%$//' | calc_stat | |
} | |
stat_time() { | |
printf " %-40s" "${1//h:mm:ss or m:ss/seconds}:" | |
extract_values "$1" | awk '{ | |
split($0, arr, ":") | |
if (length(arr) == 1) { | |
total_seconds = arr[1] | |
} else if (length(arr) == 2) { | |
total_seconds = arr[1] * 60 + arr[2] | |
} else if (length(arr) == 3) { | |
total_seconds = arr[1] * 3600 + arr[2] * 60 + arr[3] | |
} | |
printf("%s\n", total_seconds) | |
}' | calc_stat | |
} | |
stat_status() { | |
printf " %s:" "$1" | |
extract_values "$1" | sort -n | uniq -c | awk '{ | |
printf $1 >= 2 ? " %s (%s times)" : " %s", $2, $1 | |
} | |
END { | |
printf "\n" | |
}' | |
} | |
strip_whitespace() { | |
cat | sed 's/^[[:space:]]*\|[[:space:]]*$//g' | |
} | |
extract_values() { | |
cat | grep "$1" | sed "s|$1 *: *||" | strip_whitespace | |
} | |
calc_stat() { | |
cat | awk '{ | |
if (min > $1) min = $1 | |
if (max < $1) max = $1 | |
sum += $1 | |
sumsq += ($1)^2 | |
count++ | |
} | |
BEGIN { | |
min = 1e99 | |
max = -1e99 | |
} | |
END { | |
mean = sum / count | |
var = (sumsq - sum^2 / count) / (count - 1) | |
sd = sqrt(var) | |
printf "%14.3f (%12.3f) %14.9g %14.9g\n", mean, sd, min, max | |
}' | |
} | |
( | |
run pushd "$SCRIPT_DIR" | |
if [ -n "$VERSION" ]; then | |
if [ ! -d "$VERSION" ]; then | |
run git worktree add --detach --force "$VERSION" "$TAG" | |
echo '*' >"$VERSION/.gitignore" | |
fi | |
run pushd "$VERSION" | |
fi | |
if [ -f configure.ac ] && { [ ! -f configure ] || { [ ! -d build-aux ] && [ ! -f config.sub ] ; }; }; then | |
run autoreconf -ifv | |
fi | |
if [ ! -d "$BUILD_DIR" ]; then | |
run mkdir -p "$BUILD_DIR" | |
if [ ! -f "$BUILD_DIR/.gitignore" ]; then | |
echo '*' >"$BUILD_DIR/.gitignore" | |
fi | |
fi | |
if [ ! -f "$BUILD_DIR/Makefile" ]; then | |
run pushd "$BUILD_DIR" | |
# shellcheck disable=SC2086 | |
run ../configure $CONFIG_OPTS | |
run popd | |
fi | |
if $DO_CLEAN; then | |
run make -C "$BUILD_DIR" clean | |
exit | |
fi | |
if [ -n "$MAKE_OPTS" ]; then | |
run make -C "$BUILD_DIR/sources" "$TARGET" -j "$($NPROC)" "$MAKE_OPTS" | |
else | |
run make -C "$BUILD_DIR/sources" "$TARGET" -j "$($NPROC)" | |
fi | |
) | |
if [ -z "$VERSION" ]; then | |
builddir="$SCRIPT_DIR/$BUILD_DIR" | |
else | |
builddir="$SCRIPT_DIR/$VERSION/$BUILD_DIR" | |
fi | |
target="$builddir/sources/$TARGET" | |
if $DO_COVERAGE; then | |
run "$LCOV" -z -d "$builddir" | |
fi | |
if $DO_CHECK; then | |
if $DO_VALGRIND; then | |
run "$SCRIPT_DIR/check/check.rb" --form "$target" --valgrind "$VALGRIND" --valgrind-opts "$VALGRIND_OPTS" --stat "$@" | |
else | |
run "$SCRIPT_DIR/check/check.rb" --form "$target" --stat "$@" | |
fi | |
elif $DO_TIME; then | |
case $N in | |
default|1) | |
run "$TIME" -v "$target" "$@" | |
;; | |
*) | |
times=() | |
for ((i=1; i<=N; i++)); do | |
s=$({ run "$TIME" -v "$target" "$@" 1>&3; } 2>&1) | |
times+=("$s"$'\n') | |
done 3>&1 | |
echo >&2 | |
analyze_stat "$N" "${times[@]}" >&2 | |
;; | |
esac | |
else | |
if [ $# -ge 1 ]; then | |
case $TARGET in | |
parform|parvorm) | |
if $DO_VALGRIND; then | |
# shellcheck disable=SC2086 | |
run "$MPIRUN" $MPIRUN_OPTS "$VALGRIND" $VALGRIND_OPTS "$target" "$@" | |
else | |
# shellcheck disable=SC2086 | |
run "$MPIRUN" $MPIRUN_OPTS "$target" "$@" | |
fi | |
;; | |
*) | |
if $DO_VALGRIND; then | |
# shellcheck disable=SC2086 | |
run "$VALGRIND" $VALGRIND_OPTS "$target" "$@" | |
else | |
run "$target" "$@" | |
fi | |
;; | |
esac | |
fi | |
fi | |
if $DO_COVERAGE; then | |
run "$LCOV" -c -d "$builddir" -o "$builddir/coverage.info" --gcov-tool "$GCOV" | |
run "$LCOV" -r "$builddir/coverage.info" "$GCC_VERSION" -o "$builddir/coverage.info" --ignore-errors unused | |
run "$LCOV" -r "$builddir/coverage.info" "/usr/include/" -o "$builddir/coverage.info" --ignore-errors unused | |
run "$LCOV" -r "$builddir/coverage.info" "/include/bits/" -o "$builddir/coverage.info" --ignore-errors unused | |
run "$GENHTML" -o "$builddir/html" "$builddir/coverage.info" | |
echo "Output: $(ls -l "$builddir/html/index.html")" | |
fi | |
if $DO_GPROF; then | |
# NOTE: "run" doesn't work with pipes. | |
_run_echo "$GPROF $target | $GPROF2DOT | $DOT -Tpng -o gprof.png" | |
"$GPROF" "$target" | "$GPROF2DOT" | "$DOT" -Tpng -o gprof.png | |
echo "Output: $(ls -l "$(pwd)/gprof.png")" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment