Two updates suggested by Paul Koning: - Enable building multiple simulator targets using a comma-separated list, e.g.: pdp8,pdp11,3b2 - Unconditionally clean CMake's cache each time the script is invoked. This eliminates confusing CMake configuration messages when CMake changes or updates, and CMake policies change. The most recent policy change was IMPORT_LOCATION, which is now mandatory for interface libraries. An old CMake configuration cache doesn't reflect the import library IMPORT_LOCATION property, which caused confusing warning messages. The cure is removing the old cache and reconfiguring.
395 lines
12 KiB
Bash
Executable file
395 lines
12 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
##-- Bash functions --
|
|
showHelp()
|
|
{
|
|
[ x"$1" != x ] && { echo "${scriptName}: $1"; echo ""; }
|
|
cat <<EOF
|
|
Configure and build simh simulators on Linux and *nix-like platforms.
|
|
|
|
Subdirectories:
|
|
cmake/build-unix: Makefile-based build simulators
|
|
cmake/build-ninja: Ninja build-based simulators
|
|
|
|
Options:
|
|
--------
|
|
--clean (-x) Remove the build subdirectory
|
|
--generate (-g) Generate the build environment, don't compile/build
|
|
--parallel (-p) Enable build parallelism (parallel builds)
|
|
--nonetwork Build simulators without network support
|
|
--novideo Build simulators without video support
|
|
--notest Do not execute 'ctest' test cases
|
|
--noinstall Do not install SIMH simulators.
|
|
--testonly Do not build, execute the 'ctest' test cases
|
|
--installonly Do not build, install the SIMH simulators
|
|
|
|
--flavor (-f) Specifies the build flavor. Valid flavors are:
|
|
unix
|
|
ninja
|
|
xcode
|
|
xcode-universal
|
|
msys
|
|
msys2
|
|
mingw
|
|
ucrt
|
|
--config (-c) Specifies the build configuration: 'Release' or 'Debug'
|
|
|
|
--target Build a specific simulator or simulators. Separate multiple
|
|
targets with a comma, e.g. "--target pdp8,pdp11,vax750,altairz80,3b2"
|
|
--lto Enable Link Time Optimization (LTO) in Release builds
|
|
--debugWall Enable maximal warnings in Debug builds
|
|
--cppcheck Enable cppcheck static code analysis rules
|
|
|
|
--cpack_suffix Specify CPack's packaging suffix, e.g., "ubuntu-22.04"
|
|
to produce the "simh-4.1.0-ubuntu-22.04.deb" Debian
|
|
package.
|
|
|
|
--verbose Turn on verbose build output
|
|
|
|
--help (-h) Print this help.
|
|
EOF
|
|
|
|
exit 1
|
|
}
|
|
|
|
scriptName=$0
|
|
generateArgs=
|
|
buildArgs=
|
|
buildPostArgs=""
|
|
buildClean=
|
|
buildFlavor="Unix Makefiles"
|
|
buildSubdir=build-unix
|
|
buildConfig=Release
|
|
testArgs=
|
|
notest=no
|
|
buildParallel=no
|
|
generateOnly=
|
|
testOnly=
|
|
noinstall=
|
|
installOnly=
|
|
verboseMode=
|
|
simTarget=
|
|
cpack_suffix=
|
|
|
|
## CMake supports "-S" flag (implies -B works as well.) Otherwise, it's
|
|
## the older invocation command line.
|
|
cmakeSFlag=
|
|
|
|
## This script really needs GNU getopt. Really. And try reallly hard to
|
|
## find the version that supports "--long-opt"
|
|
##
|
|
## MacOS workaround: MacOS has an older getopt installed in /usr/bin, brew
|
|
## has an updated version that installs in a custom place.
|
|
[[ -d /usr/local/opt/gnu-getopt/bin ]] && PATH="/usr/local/opt/gnu-getopt/bin:$PATH"
|
|
[[ -d /opt/homebrew/opt/gnu-getopt/bin ]] && PATH="/opt/homebrew/opt/gnu-getopt/bin:$PATH"
|
|
[[ -d /usr/local/opt/coreutils/libexec/gnubin ]] && PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
|
|
|
|
getopt_prog=
|
|
IFS_SAVE="${IFS}"; IFS=":"; for p in ${PATH}; do
|
|
"${p}/getopt" -T > /dev/null 2>&1
|
|
if [[ $? -eq 4 ]]; then
|
|
getopt_prog="${p}/getopt"
|
|
break
|
|
fi
|
|
done
|
|
IFS="${IFS_SAVE}"
|
|
|
|
if [[ "x${getopt_prog}" = "x" ]]; then
|
|
echo "${scriptName}: GNU getopt needed for this script to function properly."
|
|
echo "${scriptName}: Specifically, a 'getopt' that supports the '-T' flag (enhanced getopt)"
|
|
exit 1
|
|
fi
|
|
|
|
## This script also needs GNU coreutils
|
|
realpath=$(which realpath) || {
|
|
echo "${scriptName}: Could not find 'realpath'. Please install and re-execute this script."
|
|
echo "${scriptName}: 'realpath' is a component of the GNU coreutils collection."
|
|
}
|
|
dirname=$(which dirname) || {
|
|
echo "${scriptName}: Could not find 'dirname'. Please install and re-execute this script."
|
|
echo "${scriptName}: 'dirname' is a component of the GNU coreutils collection."
|
|
}
|
|
|
|
## Check if CMake supports parallel
|
|
cmake=$(which cmake) || {
|
|
echo "${scriptName}: Could not find 'cmake'. Please install and re-execute this script."
|
|
exit 1
|
|
}
|
|
|
|
ctest=$(which ctest) || {
|
|
echo "${scriptName}: Could not find 'ctest'. Please check your 'cmake' installation."
|
|
exit 1
|
|
}
|
|
|
|
echo "** $(${cmake} --version)"
|
|
|
|
$(${cmake} -h 2>&1 | grep -- "-S" > /dev/null) && {
|
|
cmakeSFlag=yes
|
|
}
|
|
|
|
canParallel=no
|
|
(${cmake} --build /tmp --help 2>&1 | grep parallel > /dev/null) && {
|
|
canParallel=yes
|
|
}
|
|
|
|
canTestParallel=no
|
|
# (${ctest} --help 2>&1 | grep parallel > /dev/null) && {
|
|
# canTestParallel=yes
|
|
# }
|
|
|
|
if [[ "x${MSYSTEM}" != x ]]; then
|
|
case "${MSYSTEM}" in
|
|
MSYS|MINGW64)
|
|
buildFlavor="MinGW Makefiles"
|
|
buildSubdir=build-mingw
|
|
;;
|
|
UCRT64)
|
|
buildFlavor="Ninja"
|
|
buildSubdir=build-ninja
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
longopts=clean,help,flavor:,config:,nonetwork,novideo,notest,parallel,generate,testonly
|
|
longopts=${longopts},noinstall,installonly,verbose,target:,lto,debugWall,cppcheck,cpack_suffix:
|
|
|
|
ARGS=$(${getopt_prog} --longoptions $longopts --options xhf:cpg -- "$@")
|
|
if [ $? -ne 0 ] ; then
|
|
showHelp "${scriptName}: Usage error (use -h for help.)"
|
|
fi
|
|
|
|
eval set -- ${ARGS}
|
|
while true; do
|
|
case $1 in
|
|
-x | --clean)
|
|
buildClean=yes; shift
|
|
;;
|
|
-h | --help)
|
|
showHelp
|
|
;;
|
|
-f | --flavor)
|
|
case "$2" in
|
|
unix)
|
|
buildFlavor="Unix Makefiles"
|
|
buildSubdir=build-unix
|
|
shift 2
|
|
;;
|
|
ninja|ucrt64)
|
|
buildFlavor=Ninja
|
|
buildSubdir=build-ninja
|
|
shift 2
|
|
;;
|
|
xcode)
|
|
buildFlavor=Xcode
|
|
buildSubdir=build-xcode
|
|
shift 2
|
|
;;
|
|
xcode-universal)
|
|
buildFlavor=Xcode
|
|
buildSubdir=build-xcode-universal
|
|
generateArgs="${generateArgs} -DMAC_UNIVERSAL:Bool=On"
|
|
shift 2
|
|
;;
|
|
mingw|mingw64|msys|msys2)
|
|
buildFlavor="MinGW Makefiles"
|
|
buildSubdir=build-mingw
|
|
shift 2
|
|
;;
|
|
*)
|
|
showHelp "Invalid build flavor: $2"
|
|
;;
|
|
esac
|
|
;;
|
|
-c | --config)
|
|
case "$2" in
|
|
Release|Debug)
|
|
buildConfig=$2
|
|
shift 2
|
|
;;
|
|
*)
|
|
showHelp "Invalid build configuration: $2"
|
|
;;
|
|
esac
|
|
;;
|
|
--nonetwork)
|
|
generateArgs="${generateArgs} -DWITH_NETWORK:Bool=Off"
|
|
shift
|
|
;;
|
|
--novideo)
|
|
generateArgs="${generateArgs} -DWITH_VIDEO:Bool=Off"
|
|
shift
|
|
;;
|
|
--notest)
|
|
notest=yes
|
|
shift
|
|
;;
|
|
--noinstall)
|
|
noinstall=yes
|
|
shift
|
|
;;
|
|
--lto)
|
|
generateArgs="${generateArgs} -DRELEASE_LTO:Bool=On"
|
|
shift
|
|
;;
|
|
--debugWall)
|
|
generateArgs="${generateArgs} -DDEBUG_WALL:Bool=On"
|
|
shift
|
|
;;
|
|
--cppcheck)
|
|
generateArgs="${generateArgs} -DENABLE_CPPCHECK:Bool=On"
|
|
shift
|
|
;;
|
|
--cpack_suffix)
|
|
generateArgs="${generateArgs} -DSIMH_PACKAGE_SUFFIX=$2"
|
|
shift 2
|
|
;;
|
|
-p | --parallel)
|
|
buildParallel=yes
|
|
shift
|
|
;;
|
|
-g | --generate)
|
|
generateOnly=yes
|
|
shift
|
|
;;
|
|
--testonly)
|
|
testOnly=yes
|
|
shift
|
|
;;
|
|
--installonly)
|
|
installOnly=yes
|
|
shift
|
|
;;
|
|
--verbose)
|
|
verboseMode="--verbose"
|
|
shift
|
|
;;
|
|
--target)
|
|
noinstall=yes
|
|
simTarget="${simTarget} $2"
|
|
shift 2
|
|
;;
|
|
--)
|
|
## End of options. we'll ignore.
|
|
shift
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
## Determine the SIMH top-level source directory:
|
|
simhTopDir=$(${dirname} $(${realpath} $0))
|
|
while [ "x${simhTopDir}" != x -a ! -f "${simhTopDir}/CMakeLists.txt" ]; do
|
|
simhTopDir=$(${dirname} "${simhTopDir}")
|
|
done
|
|
|
|
if [[ "x${simhTopDir}" = x ]]; then
|
|
echo "${scriptName}: Can't determine SIMH top-level source directory."
|
|
echo "Did this really happen?"
|
|
exit 1
|
|
else
|
|
buildSubdir=$(${realpath} "${simhTopDir}/cmake/")"/${buildSubdir}"
|
|
echo "${scriptName}: SIMH top-evel directory: ${simhTopDir}"
|
|
echo "${scriptName}: Build directory: ${buildSubdir}"
|
|
fi
|
|
|
|
if [[ x"$buildClean" != x ]]; then
|
|
echo "${scriptName}: Cleaning ${buildSubdir}"
|
|
rm -rf ${buildSubdir}
|
|
fi
|
|
if [[ ! -d ${buildSubdir} ]]; then
|
|
mkdir ${buildSubdir}
|
|
fi
|
|
|
|
## Setup test arguments (and add parallel later)
|
|
testArgs="-C ${buildConfig} --timeout 180 --output-on-failure"
|
|
|
|
## Parallel only applies to the unix flavor. GNU make will overwhelm your
|
|
## machine if the number of jobs isn't capped.
|
|
if [[ x"$canParallel" = xyes ]] ; then
|
|
if [ x"$buildParallel" = xyes -a "$buildFlavor" != Ninja ] ; then
|
|
(${cmake} --build . --help 2>&1 | grep parallel 2>&1 > /dev/null) && {
|
|
buildArgs="${buildArgs} --parallel"
|
|
buildPostArgs="${buildPostArgs} -j 8"
|
|
}
|
|
|
|
# Don't execute ctest in parallel...
|
|
# [ x${canTestParallel} = xyes ] && {
|
|
# testArgs="${testArgs} --parallel 4"
|
|
# }
|
|
fi
|
|
else
|
|
buildParallel=
|
|
fi
|
|
|
|
if [[ x"${simTarget}" != x ]]; then
|
|
simTests=""
|
|
for tgt in $(echo ${simTarget} | sed 's/,/ /g'); do
|
|
buildArgs="${buildArgs} --target ${tgt}"
|
|
[[ x"${simTests}" != x ]] && simTests="${simTests}|"
|
|
simTests="${simTests}${tgt}"
|
|
done
|
|
testArgs="${testArgs} -R simh-(${simTests})\$"
|
|
fi
|
|
|
|
buildArgs="${buildArgs} --config ${buildConfig}"
|
|
|
|
if [[ x$generateOnly = xyes ]]; then
|
|
phases=generate
|
|
elif [[ x$testOnly = xyes ]]; then
|
|
phases=test
|
|
elif [[ x$installOnly = xyes ]]; then
|
|
phases=install
|
|
else
|
|
phases="generate build"
|
|
if [[ x${notest} != xyes ]]; then
|
|
phases="${phases} test"
|
|
fi
|
|
fi
|
|
|
|
for ph in ${phases}; do
|
|
case $ph in
|
|
generate)
|
|
## Uncondintionally remove the CMake cache.
|
|
echo "${scriptName}: Removing CMakeCache.txt and CMakeFiles"
|
|
rm -rf ${buildSubdir}/CMakeCache.txt ${buildSubdir}/CMakefiles
|
|
|
|
if [[ "x${cmakeSFlag}" != x ]]; then
|
|
echo "${cmake} -G "\"${buildFlavor}\"" -DCMAKE_BUILD_TYPE="${buildConfig}" -S "${simhTopDir}" -B ${buildSubdir} ${generateArgs}"
|
|
${cmake} -G "${buildFlavor}" -DCMAKE_BUILD_TYPE="${buildConfig}" -S "${simhTopDir}" -B "${buildSubdir}" ${generateArgs} || { \
|
|
echo "*** ${scriptName}: Errors detected during environment generation. Exiting."
|
|
exit 1
|
|
}
|
|
else
|
|
echo "${cmake} -G "\"${buildFlavor}\"" -DCMAKE_BUILD_TYPE="${buildConfig}" "${simhTopDir}" ${generateArgs}"
|
|
( cd "${buildSubdir}"; \
|
|
${cmake} -G "${buildFlavor}" -DCMAKE_BUILD_TYPE="${buildConfig}" "${simhTopDir}" ${generateArgs}) || { \
|
|
echo "*** ${scriptName}: Errors detected during environment generation. Exiting.";
|
|
exit 1
|
|
}
|
|
fi
|
|
;;
|
|
build)
|
|
${cmake} --build "${buildSubdir}" ${buildArgs} ${verboseMode} -- ${buildPostArgs} || {
|
|
echo "*** ${scriptName}: Build errors detected. Exiting."
|
|
exit 1
|
|
}
|
|
;;
|
|
test)
|
|
(cd "${buildSubdir}" \
|
|
&& echo ${ctest} ${testArgs} ${verboseMode} \
|
|
&& ${ctest} ${testArgs} ${verboseMode}) || {
|
|
echo "*** ${scriptName}: Errors detected during testing. Exiting."
|
|
exit 1
|
|
}
|
|
;;
|
|
install)
|
|
${cmake} --build "${buildSubdir}" --target install --config "${buildConfig}"
|
|
;;
|
|
package)
|
|
(cd "${buildSubdir}" \
|
|
&& ${cpack} -G ZIP -C ${buildConfig} ${verboseMode} \
|
|
&& mv *.zip ${simhTopDir}/PACKAGES \
|
|
)
|
|
;;
|
|
esac
|
|
done
|