Skip to content

Instantly share code, notes, and snippets.

@tamaskenez
Created December 9, 2015 00:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tamaskenez/d4509f240f4224eb9853 to your computer and use it in GitHub Desktop.
Save tamaskenez/d4509f240f4224eb9853 to your computer and use it in GitHub Desktop.
Execute multiple `cmake` commands with a single `cmakex` command.
#!/bin/bash -e
#
# cmakex
# ======
#
# Execute multiple `cmake` commands with a single `cmakex` command.
# Also, enable the CMAKE_INSTALL_PREFIX environment variable
#
# SYNOPSYS
# --------
#
# cmakex [c][b][i][t][d][r][w] [cmake-options...]
#
# The first (compulsory) parameter is a single word (the command word)
# which is composed of the letters listed above. It is followed by options
# for the `cmake` command (configure and build steps)
#
# DESCRIPTION
# -----------
#
# The three dimensions of the cmake comands are:
#
# 1. steps (configure, build/install, test)
# 2. configuratons (Debug, Release, ...)
# 3. targets
#
# You can use the `cmakex` command to execute multiple `cmake` commands
# for the specified steps, configurations and targets.
#
# As a quick example, here's how to configure, install and test a project
# from scrach, for `Debug` and `Release` configurations:
#
# cd project_source_dir
# CMAKE_INSTALL_PREFIX=$PWD/out cmakex citdr -H. -Bb
#
# `cmakex` also hides the difference between regular and multiconfig
# generators (Visual Studio and Xcode) regarding the `--config` and
# `-DCMAKE_BUILD_TYPE` options.
#
# As an added convenience it forwards the values of the
# CMAKE_PREFIX_PATH, CMAKE_INSTALL_PREFIX and CMAKE_MODULE_PATH
# environment variable to the CMake configuration
# step (by setting these as cache variables)
#
# Note: CMAKE_PREFIX_PATH as an environment variable is already supported
# by `cmake` but is not remembered in the cache. See details below.
#
# The Command Word
# ----------------
#
# The first parameter is the command word. The order of the letters is not
# important. The meanings of the letters are:
#
# - c: Configure step, that is `cmake ...` (no `--build`)
# - b: Build step, that is `cmake --build ...`
# - i: Install step, that is `cmake --build ... --target install ...`
# - t: Test step, that is `ctest`
# - d, r, w:
# Configurations `Debug`, `Release` and `RelWithDebInfo`
# Multiple configurations can be specified.
#
# CMake Options
# -------------
#
# After the command word you can specify
#
# - any options the cmake command accepts (the normal, non-build mode)
# - `--target <tgt>` (also multiple times)
# - `--config <cfg>` (also multiple times)
# - `--clean-first`
# - double-dash "--" followed by options to the native build tool
#
# CMAKE_INSTALL_PREFIX, CMAKE_PREFIX_PATH, CMAKE_MODULE_PATH
# ----------------------------------------------------------
#
# When calling the `cmake` configuration step, if any of these environment
# variables are defined, `cmakex` adds it to the command line:
#
# -DCMAKE_INSTALL_PREFIX=$CMAKE_INSTALL_PREFIX
# -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH
# -DCMAKE_MODULE_PATH=$CMAKE_MODULE_PATH
#
# If an environment variable is not defined or empty than it won't be
# removed from the cache.
#
# Note that CMAKE_PREFIX_PATH is already supported by `cmake` as an
# environment variable but it's not retained in the cache. With `cmakex`
# it will be.
#
# You need to specify the 'c' step for this to work. The possible automatic
# cmake reconfiguration before `--build` will not pick up the environment
# variables.
#
# Examples:
# ---------
#
# Install the 'Debug' and 'Release' configs:
#
# cmakex cidr -Hsource_dir -Bbuild_dir -DMY_OPTION=something
#
# Test the 'Release' config:
#
# cd build_dir
# cmakex tr .
#
# Note, that the configure/build steps are omitted in that case.
# To execute the build step, use:
#
# cd build_dir
# cmakex btr .
#
# To test a project which has not been configured yet:
#
# cd build_dir
# cmakex cbtr .
#
# or
#
# cmakex cbtr -Hsource_dir -Bbuild_dir
#
if [[ -z $1 ]]; then
echo "No arguments. For help see this file ($0)" >&2
exit 1
fi
command_word=$1
shift
unparsed=$command_word
for c in c b i t r d w; do
eval unset arg_$c
done
while [[ -n $unparsed ]]; do
c=${unparsed:0:1}
unparsed=${unparsed:1}
case $c in
c ) arg_c=1;;
b ) arg_b=1;;
i ) arg_i=1;;
t ) arg_t=1;;
r ) arg_r=1;;
d ) arg_d=1;;
w ) arg_w=1;;
* )
echo "Invalid char in first argument: $c" >&2
exit 1
esac
done
unset configs
if [[ -n $arg_d ]]; then
configs="$configs Debug"
fi
if [[ -n $arg_w ]]; then
configs="$configs RelWithDebInfo"
fi
if [[ -n $arg_r ]]; then
configs="$configs Release"
fi
unset binary_dir
unset source_dir
unset config_args
unset build_args
unset native_tool_args
unset build_targets
unset config_args_besides_binary_dir
if [[ -n $arg_i ]]; then
build_targets="$build_targets install"
fi
while (( $# )); do
if [[ -z ${native_tool_args+1} ]]; then
if [[ "$1" == "--" ]]; then
native_tool_args=('--')
elif [[ "$1" == "--target" ]]; then
shift
if [[ -n "$1" ]]; then
build_targets="$build_targets $1"
else
echo "Missing target name after '--target'" >&2
exit 1
fi
elif [[ "$1" == "--config" ]]; then
shift
if [[ -n "$1" ]]; then
configs="$configs $1"
else
echo "Missing config name after '--config'" >&2
exit 1
fi
elif [[ "$1" =~ ^(--clean-first|--use-stderr)$ ]]; then
build_args+=("$1")
else
config_args+=("$1")
if [[ "$1" =~ ^-H.+ ]]; then
source_dir="${1:2}"
config_args_besides_binary_dir=1
elif [[ "$1" =~ ^-B.+ ]]; then
binary_dir="${1:2}"
elif [[ "$1" =~ ^(-C|-D|-U|-G|-T|-A)$ ]]; then
shift
config_args+=("$1")
config_args_besides_binary_dir=1
elif [[ "${1:0:1}" != '-' ]]; then
if [[ -f "$1/CMakeLists.txt" ]]; then
source_dir="$1"
config_args_besides_binary_dir=1
elif [[ -f "$1/CMakeCache.txt" ]]; then
binary_dir="$1"
else
echo "$1 is neither a valid source dir (no CMakeLists.txt), " \
"nor an existing binary dir (no CMakeCache.txt)" >&2
exit 1
fi
else
config_args_besides_binary_dir=1
fi
fi
else
native_tool_args+=("$1")
fi
shift
done
if [[ -z "$binary_dir" ]]; then
binary_dir="$PWD"
fi
if [[ -n "$source_dir" ]]; then
echo "CMAKE_SOURCE_DIR: $source_dir"
fi
echo "CMAKE_BINARY_DIR: $binary_dir"
if [[ -n "$build_targets" ]]; then
echo "targets: $build_targets"
fi
if [[ -n "$configs" ]]; then
echo "configurations: $configs"
fi
#echo "config_args: ${config_args[@]}"
#echo "build_args: ${build_args[@]}"
#echo "native_tool_args: ${native_tool_args[@]}"
if [[ -z "$configs" ]]; then
configs=-
fi
if [[ -n $arg_b && -z "$build_targets" ]]; then
build_targets=-
fi
unset config_env_arg
if [[ -n "$CMAKE_INSTALL_PREFIX" ]]; then
config_env_arg+=("-DCMAKE_INSTALL_PREFIX:PATH=$CMAKE_INSTALL_PREFIX")
fi
if [[ -n "$CMAKE_PREFIX_PATH" ]]; then
config_env_arg+=("-DCMAKE_PREFIX_PATH:STRING=$CMAKE_PREFIX_PATH")
fi
if [[ -n "$CMAKE_MODULE_PATH" ]]; then
config_env_arg+=("-DCMAKE_MODULE_PATH:STRING=$CMAKE_MODULE_PATH")
fi
for config in $configs; do
if [[ $config == - ]]; then
unset config_config_arg
unset build_config_arg
unset test_config_arg
else
config_config_arg="-DCMAKE_BUILD_TYPE=$config"
build_config_arg="--config $config"
test_config_arg="-C $config"
fi
# configure step
if [[ -n $arg_c ]]; then
(set -x; cmake $config_config_arg "${config_env_arg[@]}" "${config_args[@]}")
elif [[ -n $config_args_besides_binary_dir ]]; then
echo -e "You specified args for the cmake configuration step besides binary dir:\n"\
"\t${config_args[@]}\n"\
"but the 'c' option is missing from the command word: \"$command_word\"" >&2
exit 1
fi
# build step
for target in $build_targets; do
if [[ $target == - ]]; then
unset build_target_arg
else
build_target_arg="--target $target"
fi
echo "bta $build_target_arg"
(set -x; cmake --build "$binary_dir" $build_args $build_target_arg $build_config_arg "${native_tool_args[@]}")
done
# test step
if [[ -n $arg_t ]]; then
pushd "$binary_dir" >/dev/null
(set -x; ctest $test_config_arg)
popd >/dev/null
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment