#!/bin/bash ##-- Bash functions -- showHelp() { [ x"$1" != x ] && { echo "${scriptName}: $1"; echo ""; } cat < /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: longopts=${longopts},cache,no-aio,no-aio-intrinsics ARGS=$(${getopt_prog} --longoptions $longopts --options xhf:c:pg -- "$@") 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 ;; --no-aio) generateArgs="${generateArgs} -DWITH_ASYNC:Bool=Off" shift ;; --no-aio-intrinsics) generateArgs="${generateArgs} -DDONT_USE_AIO_INTRINSICS:Bool=On" 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 ;; --cache) generateOnly=yes generateArgs="${generateArgs} -LA" 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 # Sanity check: buildSubdir should be set, unless the '-f' flag wasn't present. if [ "x${buildSubdir}" = x ]; then echo "" echo "${scriptName}: Build flavor is NOT SET -- see the \"--flavor\"/\"-f\" flag in the help." echo "" showHelp fi ## 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