| #!/bin/bash | |
| # | |
| # Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) | |
| # | |
| # SPDX-License-Identifier: GPL-2.0-only | |
| # | |
| # Note: This script uses bash for its execution, but not because it uses any | |
| # Bashisms; instead, it is to work around a POSIX-violating bug in signal | |
| # handling in dash, which is the /bin/sh on most Debian-based systems. See | |
| # <https://bugs.debian.org/779416>, filed in 2015 and still not fixed (posh has | |
| # the same problem; ksh, mksh, yash, and zsh do not). | |
| # Note: This is _not_ a legitimate parser for CMakeLists! It is quite crude. | |
| # TODO: Rewrite this in Python! | |
| set -eu | |
| SOURCE_ROOT=${0%/*} | |
| PROGNAME=${0##*/} | |
| REPO_DIR=.repo | |
| CMAKE_COMPILER_DEFAULT=gcc | |
| CMAKE_COMPILER=$CMAKE_COMPILER_DEFAULT | |
| CMAKELISTS="$SOURCE_ROOT"/CMakeLists.txt | |
| CMAKECACHE=CMakeCache.txt | |
| CMAKETOOLCHAIN="$SOURCE_ROOT"/kernel/"$CMAKE_COMPILER".cmake | |
| DO_CMAKE_INITIALIZE= | |
| EASY_KNOBS="$SOURCE_ROOT"/easy-settings.cmake | |
| CMAKE_ARGS= | |
| # Set to a non-null string (like "yes") to enable debugging output. | |
| DEBUG_MATCHER= | |
| MODE=invoke | |
| # We use the following exit status conventions: | |
| # 0: normal operation, successful, "true" | |
| # 1: expected failure, "false" | |
| # 2: usage error | |
| # 3: other error | |
| EXIT_STATUS=3 | |
| # Set up terminal capabilities (for displaying in bold and colors). | |
| # | |
| # See terminfo(5) for a list of terminal capability strings. | |
| # | |
| # tput returns an empty string (and exits with a nonzero status) for | |
| # unsupported string capabilities, and -1 for unsupported integer | |
| # capablilities. | |
| BOLD=$(tput bold) || BOLD= | |
| NORMAL=$(tput sgr0) || NORMAL= | |
| NCOLORS=$(tput colors) | |
| # If the terminal doesn't support color at all, these will remain null. | |
| RED= | |
| GREEN= | |
| YELLOW= | |
| CYAN= | |
| # We want different foreground color numbers if we have a terminal capable of | |
| # more than 8, because generally the contrast is bad if we use the low-numbered | |
| # colors (bold helps, but only so much). On terminals truly capable of only 8 | |
| # colors, we have to rely on the implementation to provide good contrast. | |
| if [ -n "$NCOLORS" ] | |
| then | |
| if [ $NCOLORS -gt 8 ] | |
| then | |
| RED=$(tput setaf 9) | |
| GREEN=$(tput setaf 10) | |
| YELLOW=$(tput setaf 11) | |
| CYAN=$(tput setaf 14) | |
| # This is an exact equality match on purpose. tput will report -1 for a | |
| # truly monochrome terminal and in that case we don't want to mess with | |
| # the setaf capability at all. | |
| elif [ $NCOLORS -eq 8 ] | |
| then | |
| RED=$(tput setaf 1) | |
| GREEN=$(tput setaf 2) | |
| YELLOW=$(tput setaf 3) | |
| CYAN=$(tput setaf 6) | |
| fi | |
| fi | |
| # Emit diagnostic message. | |
| # @params: a set of strings comprising a human-intelligible message | |
| # | |
| # Display the diagnostic message itself in bold. | |
| _print () { | |
| echo "${PROGNAME:-(unknown program)}: $BOLD$*$NORMAL" | |
| } | |
| # Emit debugging message to standard error. | |
| # @params: a set of strings comprising a human-intelligible message | |
| debug () { | |
| _print "${CYAN}debug: $*" >&2 | |
| } | |
| # Emit informational message to standard error. | |
| notice () { | |
| _print "${GREEN}notice: $*" >&2 | |
| } | |
| # Emit warning message to standard error. | |
| warn () { | |
| _print "${YELLOW}warning: $*" >&2 | |
| } | |
| # Emit error message to standard error. | |
| fail () { | |
| _print "${RED}error: $*" >&2 | |
| } | |
| # Report unrecoverable error and terminate script. | |
| # @params: a set of strings comprising a human-intelligible message | |
| # | |
| # Note: $EXIT_STATUS, if set in the invoking scope, determines the exit status | |
| # used by this function. | |
| die () { | |
| _print "${RED}fatal error: $*" >&2 | |
| exit ${EXIT_STATUS:-3} | |
| } | |
| # [debugging] Report how the input line was classified. | |
| # @params: a string describing the classification | |
| describe () { | |
| test -n "$DEBUG_MATCHER" && debug "$CMAKE_LINENO: $*" || : | |
| } | |
| # Back up the CMake cache file, re-run CMake, and see if the new cache file | |
| # differs from the backup. If it does, the configuration is not stable and we | |
| # will warn about it (see end of script). | |
| # | |
| # Returns 0 (success) if the files are the same; 1 if they differ; other if | |
| # trouble. | |
| is_configuration_stable () { | |
| CMAKECACHE_BACKUP=$CMAKECACHE.griddle.bak | |
| if ! [ -e $CMAKECACHE ] | |
| then | |
| die "CMake cache file \"$CMAKECACHE\" unexpectedly does not exist!" | |
| fi | |
| cp $CMAKECACHE $CMAKECACHE_BACKUP | |
| # $CMAKE_ARGS is unquoted because because cmake needs shell word-splitting | |
| # to be done on its parameters. Furthermore, there should be no | |
| # configuration variables with whitespace embedded in their flag names or | |
| # values. (Well, certainly not the _names_...) | |
| cmake $CMAKE_ARGS . || die "cmake failed" | |
| cmp -s $CMAKECACHE $CMAKECACHE_BACKUP | |
| # `return` with no arguments returns the exit status of the last "simple | |
| # command" executed, so don't insert anything between `cmp` and `return`. | |
| return | |
| } | |
| # Break up Set directive and save interesting parts. | |
| # @params: one or more strings comprising a line from a CMake input file | |
| unpack_set () { | |
| # TODO: Handle a last parameter of "FORCE". | |
| MYLINE=$* | |
| # Chop off directive. | |
| MYLINE=${MYLINE#set(} | |
| # Chop off trailing parenthesis. | |
| MYLINE=${MYLINE%)} | |
| # By turning off globbing and leaving $MYLINE unquoted, we largely get the | |
| # word-splitting we want. | |
| set -o noglob | |
| set -- $MYLINE | |
| set +o noglob | |
| CONFIG_VAR=$1 | |
| shift | |
| DEFAULT_VALUE=$1 | |
| shift | |
| if [ "$1" = "CACHE" ] | |
| then | |
| CACHED="(cached)" | |
| else | |
| CACHED="(not cached)" | |
| fi | |
| shift | |
| TYPE=$1 | |
| shift | |
| DESCRIPTION=$* | |
| # Chop off leading and trailing double quotes. | |
| DESCRIPTION=${DESCRIPTION#\"} | |
| DESCRIPTION=${DESCRIPTION%\"} | |
| } | |
| # Set the value of the variable named in $1 to the maximum of $2 and its current | |
| # value. | |
| # @params: $1: a variable name; $2: the potential new value | |
| update_field_width () { | |
| VAR=$1 | |
| # We use eval so we can get the value of the indirectly-referenced variable | |
| # in VAR. E.g., if $VAR is "CV_WIDTH", we set $OLD_WIDTH to the value of | |
| # $CV_WIDTH below. | |
| eval OLD_WIDTH=\$$VAR | |
| shift | |
| VALUE=$* | |
| NEW_WIDTH=${#VALUE} | |
| if [ $NEW_WIDTH -gt $OLD_WIDTH ] | |
| then | |
| # We use eval to assign to the variable named in $VAR. | |
| eval $VAR=$NEW_WIDTH | |
| fi | |
| } | |
| # Perform sanity checks on the environment. | |
| # Is a repo dir present in the PWD? | |
| if [ -d "$REPO_DIR" ] | |
| then | |
| die "run this tool from a build directory (e.g., \"mkdir build; cd build\")" | |
| fi | |
| # Guard against rookie mistake of running tool in some non-build subdirectory of | |
| # the repo checkout. | |
| THIS_DIR=${PWD##*/} | |
| if [ "$THIS_DIR" = kernel ] || [ "$THIS_DIR" = projects ] \ | |
| || [ "$THIS_DIR" = tools ] | |
| then | |
| die "run this tool from a build directory (e.g., \"mkdir ../build;" \ | |
| " cd ../build\")" | |
| fi | |
| # Is a repo dir present in the PWD? | |
| if ! [ -d "$SOURCE_ROOT"/"$REPO_DIR" ] | |
| then | |
| # We are completely in the wilderness. | |
| die "cannot find \"$REPO_DIR\" in this directory or its parent;" \ | |
| "${NORMAL}you need to (1) initialise a repo with \"repo init -u" \ | |
| "\$GIT_CLONE_URL\", (2) \"repo sync\", (3) create a build directory" \ | |
| "(e.g., \"mkdir build\"), (4) change into that directory (e.g." \ | |
| "\"cd build\"), and (5) try to run this tool again." | |
| fi | |
| # Is an easy config file available? | |
| if ! [ -r "$EASY_KNOBS" ] | |
| then | |
| # At this point we know we're probably in a build directory and there is a | |
| # CMake lists file, but not an easy settings file. | |
| die "\"$EASY_KNOBS\" does not exist or is not readable;" \ | |
| "${NORMAL}this project may not yet support \"$PROGNAME\"" | |
| fi | |
| CMAKE_LINENO=0 | |
| # Set up some variables to compute pleasant field widths. | |
| CV_WIDTH=0 # $CONFIG_VAR field with | |
| TY_WIDTH=0 # $TYPE field width | |
| DV_WIDTH=0 # $DEFAULT_VALUE field width | |
| while read -r LINE | |
| do | |
| CMAKE_LINENO=$((CMAKE_LINENO + 1)) | |
| # Remove syntactically unimportant leading and trailing white space. | |
| LINE=$(echo "$LINE" | sed -e 's/^\s\+//' -e 's/\s\+$//') | |
| case "$LINE" in | |
| ('#'*) | |
| describe "comment line" | |
| ;; | |
| ('') | |
| describe "blank line" | |
| ;; | |
| (set'('*) | |
| describe "configuration variable: \"$LINE\"" | |
| unpack_set "$LINE" | |
| update_field_width CV_WIDTH "$CONFIG_VAR" | |
| update_field_width TY_WIDTH "$TYPE" | |
| update_field_width DV_WIDTH "$DEFAULT_VALUE" | |
| # Save the configuration variable name as an acceptable long option | |
| # for getopt. | |
| # If the configuration variable is of boolean type, its parameter is | |
| # optional; getopt indicates that with a trailing double colon | |
| # instead of a single one. | |
| if [ "$TYPE" = BOOL ] | |
| then | |
| GETOPT_FLAGS=${GETOPT_FLAGS:+$GETOPT_FLAGS,}$CONFIG_VAR:: | |
| else | |
| GETOPT_FLAGS=${GETOPT_FLAGS:+$GETOPT_FLAGS,}$CONFIG_VAR: | |
| fi | |
| # Use eval to interpolate $CONFIG_VAR into a shell variable. For | |
| # instance, the following line might expand to: | |
| # VAR_SIMULATION_TYPE=BOOL | |
| eval "VAR_${CONFIG_VAR}_TYPE"="$TYPE" | |
| # Pack information about the configuration variable (except for | |
| # caching information) into a string to be decoded by show_usage(). | |
| # | |
| # The "records" are separated by "@@" and the "fields" by "@:". | |
| OPTIONS=${OPTIONS:+$OPTIONS@@}$CONFIG_VAR@:$TYPE@:$DEFAULT_VALUE@:$DESCRIPTION | |
| OPTION_REPORT="${OPTION_REPORT:=} | |
| $CONFIG_VAR is type: $TYPE, default: $DEFAULT_VALUE, $CACHED; $DESCRIPTION" | |
| ;; | |
| (mark_as_advanced'('*) | |
| describe "exporting external setting: \"$LINE\"" | |
| ;; | |
| (*) | |
| die "$EASY_KNOBS:$CMAKE_LINENO: I don't know how to handle \"$LINE\"" | |
| ;; | |
| esac | |
| done < "$EASY_KNOBS" | |
| # Now that we've parsed the CMakefile, we know what options we can accept. | |
| # | |
| # Append a record separator to the end of $OPTIONS for ease of processing later. | |
| OPTIONS=${OPTIONS:-}@@ | |
| # List supported target platforms. | |
| # | |
| # This function relies on the current working directory being the build | |
| # directory, but this is true by the time it is called. | |
| show_platform_help () { | |
| # This is uglier than it should be because CMake insists on its input being | |
| # seekable. So we have to set up a temporary file, ensure we write to it | |
| # only by appending, and make sure it gets cleaned up by setting up a signal | |
| # handler. Note also that CMake's message() writes to standard error. | |
| # | |
| # We quote $TEMP when dereferencing it because mktemp uses $TMPDIR, and the | |
| # user might have set that to a whitespace-containing pathname. | |
| # | |
| # We give `rm` the `-f` option in the trap handler in the event we end up | |
| # racing against the ordinary cleanup scenario. Consider: | |
| # # Clean up the temporary file and deregister the signal handler. | |
| # rm "$TEMP" | |
| # <CTRL-C> | |
| # trap - HUP INT QUIT TERM | |
| # When the user interrupts the script, the temporary file has been removed | |
| # but the signal handler has not yet been deregistered. | |
| # | |
| # This function can be greatly simplified once JIRA SELFOUR-2369 is fixed. | |
| TEMP=$(mktemp) | |
| # In our trap handler, we have to (1) do our cleanup work; (2) clear the | |
| # trap handler (restoring the default signal handler); and (3) commit | |
| # suicide so that the shell knows we exited abnormally. Unfortunately POSIX | |
| # shell offers no way of knowing which signal we are handling, short of | |
| # writing the trap handler multiple times (once for each signal); we choose | |
| # INT as our final disposition somewhat arbitrarily. | |
| # | |
| # See <https://www.cons.org/cracauer/sigint.html> for a detailed exploration | |
| # of this issue. | |
| trap 'rm -f "$TEMP"; trap - HUP INT QUIT TERM; kill -s INT $$' \ | |
| HUP INT QUIT TERM | |
| cat >> "$TEMP" <<EOF | |
| include(configs/seL4Config.cmake) | |
| foreach(val IN LISTS kernel_platforms) | |
| message("\${val}") | |
| endforeach() | |
| EOF | |
| (cd ../kernel && cmake -DCMAKE_TOOLCHAIN_FILE=ignore -P "$TEMP" 2>&1 \ | |
| | cut -d';' -f1) | |
| # Clean up the temporary file and deregister the signal handler. | |
| rm "$TEMP" | |
| trap - HUP INT QUIT TERM | |
| notice "not all seL4 projects (e.g., \"camkes\", \"sel4bench\") support" \ | |
| "all platforms" | |
| } | |
| # Display a usage message. | |
| show_usage () { | |
| # Make sure our field widths are wide enough for our column headings. | |
| update_field_width CV_WIDTH "Option" | |
| update_field_width TY_WIDTH "Type" | |
| update_field_width DV_WIDTH "Default" | |
| # Furthermore make sure the field width for the configuration flag name | |
| # itself is wide enough to accommodate the two option dashes we will add. | |
| CV_WIDTH=$(( CV_WIDTH + 2 )) | |
| cat <<EOF | |
| $PROGNAME: easy cooking with CMake | |
| $PROGNAME eases the setup of seL4-related builds by exposing only the most | |
| commonly-used configuration variables in the seL4 CMake infrastructure. These | |
| differ between projects, but you can always discover them with: | |
| $PROGNAME --help | |
| Usage: | |
| $PROGNAME [--compiler={gcc|llvm}] [CMAKE-CONFIGURATION-VARIABLE] ... | |
| $PROGNAME --help | |
| $PROGNAME --platform-help | |
| Options: | |
| --compiler={gcc|llvm} Report "gcc" or "llvm" compiler suite to CMake. | |
| (default: $CMAKE_COMPILER_DEFAULT) | |
| --help Display this message and exit. | |
| --platform-help List supported target platforms and exit. | |
| EOF | |
| if [ -z "$OPTIONS" ] | |
| then | |
| cat <<EOF | |
| The file "$EASY_KNOBS" defines no basic configuration options for this project. | |
| EOF | |
| return | |
| fi | |
| if [ -n "${GETOPT_FLAGS:+flags}" ] | |
| then | |
| echo | |
| FORMAT_STRING="%${CV_WIDTH}s %${TY_WIDTH}s %${DV_WIDTH}s %s\n" | |
| printf "$FORMAT_STRING" "Option" "Type" "Default" "Description" | |
| echo | |
| while [ -n "$OPTIONS" ] | |
| do | |
| # Unpack and display the information condensed into $OPTIONS. | |
| # | |
| # The "records" are separated by "@@" and the "fields" by "@:". | |
| # | |
| # Break off one option record at a time for clarity. | |
| RECORD=${OPTIONS%%@@*} | |
| OPTIONS=${OPTIONS#*@@} | |
| # We now have one record in $RECORD. Extract the fields. | |
| CONFIG_VAR=${RECORD%%@:*} | |
| RECORD=${RECORD#*@:} | |
| TYPE=${RECORD%%@:*} | |
| RECORD=${RECORD#*@:} | |
| DEFAULT_VALUE=${RECORD%%@:*} | |
| RECORD=${RECORD#*@:} | |
| DESCRIPTION=$RECORD | |
| printf "$FORMAT_STRING" \ | |
| "--$CONFIG_VAR" "$TYPE" "$DEFAULT_VALUE" "$DESCRIPTION" | |
| done | |
| fi | |
| } | |
| # Check the option given against those extracted from the CMake file. | |
| # @params: the option name to look up | |
| # @return: 0 (true) if option recognized; 1 (false) otherwise | |
| validate_name () { | |
| FLAG=$1 | |
| if echo "$GETOPT_FLAGS" | egrep -q "(^|.+:)?$FLAG(:.+|$)?" | |
| then | |
| return 0 | |
| else | |
| return 1 | |
| fi | |
| } | |
| # Check the option parameter given against the declared type. | |
| # @params: an option name and its parameter | |
| # @return: 0 (true) if parameter type-checks; 1 (false) otherwise | |
| # | |
| # When returning 1, be certain to issue a `fail` diagnostic. | |
| validate_parameter () { | |
| FLAG=$1 | |
| VALUE=$2 | |
| # Use eval to interpolate $FLAG into a shell variable which should have been | |
| # defined in the big case statement above (when the CMake file was parsed). | |
| # | |
| # Calling validate_name() before this function should prevent any attempt at | |
| # expanding an undefined variable name. | |
| eval TYPE=\$VAR_${FLAG}_TYPE | |
| case "$TYPE" in | |
| (BOOL) | |
| case "$VALUE" in | |
| (ON|OFF) | |
| ;; | |
| (*) | |
| fail "\"$FLAG\" only supports values of \"ON\" or \"OFF\"" | |
| return 1 | |
| ;; | |
| esac | |
| ;; | |
| (STRING) | |
| # No validation at present. | |
| ;; | |
| (*) | |
| # This is a fatal error because it indicates a limitation of the script, | |
| # not invalid user input. | |
| die "unsupported configuration variable type \"$TYPE\" (\"$FLAG\")" | |
| ;; | |
| esac | |
| return 0 | |
| } | |
| getopt -T || GETOPT_STATUS=$? | |
| if [ $GETOPT_STATUS -ne 4 ] | |
| then | |
| die "getopt from util-linux required" | |
| fi | |
| if ! ARGS=$(getopt -o '' \ | |
| --long "${GETOPT_FLAGS:+$GETOPT_FLAGS,}"compiler:,help,platform-help \ | |
| --name "$PROGNAME" -- "$@") | |
| then | |
| show_usage >&2 | |
| exit 2 | |
| fi | |
| eval set -- "$ARGS" | |
| unset ARGS | |
| HAD_ARGUMENT_PROBLEMS= | |
| while [ -n "${1:-}" ] | |
| do | |
| case "$1" in | |
| (--compiler) | |
| if [ "$2" = gcc ] || [ "$2" = llvm ] | |
| then | |
| CMAKE_COMPILER="$2" | |
| # We may be changing compilers; re-init CMake. | |
| DO_CMAKE_INITIALIZE=yes | |
| else | |
| die "unrecognized compiler \"$2\"; expected \"gcc\" or \"llvm\"" | |
| fi | |
| break | |
| ;; | |
| (--help) | |
| MODE=help | |
| shift | |
| ;; | |
| (--platform-help) | |
| MODE=platform-help | |
| shift | |
| ;; | |
| (--) | |
| shift | |
| break | |
| ;; | |
| (--*) | |
| # Strip off the argument's leading dashes. | |
| OPT=${1#--} | |
| # Reset variables set by previous iterations. | |
| FLAG= | |
| VALUE= | |
| if validate_name "$OPT" | |
| then | |
| MODE=invoke | |
| else | |
| # getopt should have caught this, but just in case... | |
| fail "unrecognized configuration option \"$1\"" | |
| HAD_ARGUMENT_PROBLEMS=yes | |
| fi | |
| VALUE=$2 | |
| # Handle the option argument. | |
| case "${VALUE:-}" in | |
| # GNU getopt synthesises a single space as an option argument if one | |
| # was not specified. | |
| (" ") | |
| fail "configuration option \"$FLAG\" must be given a value" | |
| HAD_ARGUMENT_PROBLEMS=yes | |
| ;; | |
| (*) | |
| if validate_parameter "$FLAG" "$VALUE" | |
| then | |
| CMAKE_ARGS=$CMAKE_ARGS" -D$FLAG=$VALUE" | |
| else | |
| # validate_parameter() should have issued an error message. | |
| HAD_ARGUMENT_PROBLEMS=yes | |
| fi | |
| ;; | |
| esac | |
| # Dispose of the option and option-argument pair. | |
| shift 2 | |
| # XXX: temporary hack until SELFOUR-1648 is resolved -- GBR | |
| if [ "$FLAG" = "PLATFORM" ] | |
| then | |
| case "$VALUE" in | |
| (sabre) | |
| EXTRA_ARGS="-DAARCH32=1" | |
| ;; | |
| (tk1) | |
| EXTRA_ARGS="-DAARCH32HF=1" | |
| ;; | |
| (tx[12]) | |
| EXTRA_ARGS="-DAARCH64=1" | |
| ;; | |
| esac | |
| fi | |
| # XXX: end of hack -- GBR | |
| ;; | |
| (*) | |
| die "internal error while processing options" | |
| ;; | |
| esac | |
| done | |
| if [ -n "$HAD_ARGUMENT_PROBLEMS" ] | |
| then | |
| notice "try \"$PROGNAME --help\" for option usage" | |
| exit 2 | |
| fi | |
| if ! [ -e $CMAKECACHE ] | |
| then | |
| # If the CMake cache file does not exist, call CMake with initialization | |
| # flags. | |
| DO_CMAKE_INITIALIZE=yes | |
| fi | |
| if [ $MODE = help ] | |
| then | |
| show_usage | |
| elif [ $MODE = platform-help ] | |
| then | |
| show_platform_help | |
| elif [ $MODE = invoke ] | |
| then | |
| if [ -n "$DO_CMAKE_INITIALIZE" ] | |
| then | |
| if [ -e "$CMAKELISTS" ] | |
| then | |
| # Some of these variables are unquoted because because cmake needs shell | |
| # word-splitting to be done on its parameters. Furthermore, there | |
| # should be no configuration variables with whitespace embedded in their | |
| # flag names or values. (Well, certainly not the _names_...) | |
| # $SOURCE_ROOT, however, could be anywhere in the user's file system and | |
| # its value may have embedded whitespace. | |
| cmake \ | |
| -DCMAKE_TOOLCHAIN_FILE="$CMAKETOOLCHAIN" \ | |
| -G Ninja \ | |
| ${EXTRA_ARGS:-} \ | |
| $CMAKE_ARGS \ | |
| -C "$SOURCE_ROOT/settings.cmake" \ | |
| "$SOURCE_ROOT" | |
| elif [ -e "$EASY_KNOBS" ] | |
| then | |
| # If we don't have a CMakeLists.txt in the top level project directory then | |
| # assume we use the project's directory tied to easy-settings.cmake and resolve | |
| # that to use as the CMake source directory. | |
| REAL_EASY_KNOBS=$(realpath "$EASY_KNOBS") | |
| PROJECT_DIR=${REAL_EASY_KNOBS%/*} | |
| # Initialize CMake. | |
| cmake -G Ninja ${EXTRA_ARGS:-} \ | |
| $CMAKE_ARGS \ | |
| -C "$PROJECT_DIR/settings.cmake" "$PROJECT_DIR" | |
| else | |
| # This case shouldn't be hit as if $SOURCE_ROOT/easy-settings.cmake doesn't | |
| # exist then the script should have failed earlier. | |
| die "impossible: \"$CMAKELISTS\" does not exist and \"$EASY_KNOBS\" does not either" | |
| fi | |
| fi | |
| # If this tool is re-run over an existing build with new parameters, the | |
| # CMake cache file may mutate. Warn about it if it does, and re-run CMake | |
| # until it stabilizes. | |
| THREW_STABILITY_DIAGNOSTIC= | |
| while ! is_configuration_stable | |
| do | |
| warn "configuration not stable; regenerating" | |
| THREW_STABILITY_DIAGNOSTIC=yes | |
| done | |
| if [ -n "$THREW_STABILITY_DIAGNOSTIC" ] | |
| then | |
| notice "configuration is stable" | |
| fi | |
| rm $CMAKECACHE_BACKUP | |
| else | |
| die "internal error; unrecognized operation mode \"$MODE\"" | |
| fi |