Skip to content

Instantly share code, notes, and snippets.

@mity
Last active September 12, 2026 13:29
Show Gist options
  • Select an option

  • Save mity/bb24f0625d9703911661228fab017e2a to your computer and use it in GitHub Desktop.

Select an option

Save mity/bb24f0625d9703911661228fab017e2a to your computer and use it in GitHub Desktop.
Helper CMake+Make/Ninja wrapper for MSYS2 environment.
#!/bin/bash
# Paths:
TOOLCHAIN32_ROOT="/mingw32"
TOOLCHAIN64_ROOT="/ucrt64"
#TOOLCHAIN64_ROOT="/mingw64" # legacy, deprecated
# Try to detect project root dir as a topmost dir with some of the files
# which usually mark it as a project root dir:
flist="CMakeLists.txt ChangeLog ChangeLog.txt ChangeLog.md LICENSE LICENSE.txt LICENSE.md COPYING COPYING.lib COPYING.txt COPYING.md"
d=`pwd`
while [ "$d" != "/" ]; do
for f in $flist; do
if [ -e "$d/$f" ]; then
PRJ_ROOT="$d"
fi
done
d=`dirname "$d"`
done
if [ -z "$PRJ_ROOT" ]; then
echo "Cannot find project root dir."
exit 1
fi
# Defaults:
BUILD_DIR="build"
case "$MSYSTEM" in
*64) BUILD_BITS="64";;
*) BUILD_BITS="32";;
esac
BUILD_DIR_SUFFIX="debug"
BUILD_TYPE="Debug"
TARGET=""
VERBOSE=no
# Colorize our output (if stdout is terminal):
if [ -t 1 ]; then
red="\e[1;31m"
green="\e[1;32m"
cyan="\e[1;36m"
endcolor="\e[0m"
fi
usage()
{
tool=`basename $0`
printf "${tool}: Build the current project with CMake (and Ninja or GNU Make)\n"
printf "Usage: ${tool} [option]...\n"
printf "Usage: ${tool} [option]... build-target\n"
printf "\nMain build options:\n"
printf " --32 Build 32-bit binaries (default).\n"
printf " --64 Build 64-bit binaries.\n"
printf " -d --debug Build Debug (default).\n"
printf " -r --release Build Release build.\n"
printf "\nSpecial build options:\n"
printf " -c --coverage Build with coverage profiling enabled (--coverage).\n"
printf " -p --profile Build with profiling enabled (--pg).\n"
printf " --pgo-generate Build auxiliary build for PGO (-fprofile-generate).\n"
printf " --pgo-use Build with PGO (-fprofile-use).\n"
printf " (Before using --pgo-use, the same build type has to be\n"
printf " built with --pgo-generate and THAT build must be executed\n"
printf " to collect profile data for --pgo-use.)\n"
printf "\nAuxiliary options:\n"
printf " -v --verbose Enable verbose mode.\n"
printf " -h --help Write down this help.\n"
}
while [ $# -gt 0 ]; do
case $1 in
--32 | -32 | 32 )
BUILD_BITS="32"
;;
--64 | -64 | 64 )
BUILD_BITS="64"
;;
-d | --debug | -debug | debug )
BUILD_DIR_SUFFIX="debug"
BUILD_TYPE="Debug"
;;
-r | --rel | -rel | rel | --release | -release | release )
BUILD_DIR_SUFFIX="release"
BUILD_TYPE="Release"
;;
-c | --coverage | -coverage | coverage )
ENABLE_COVERAGE=yes
;;
-p | --profile )
ENABLE_PG=yes
;;
--pgo-generate | --fprofile-generate | -fprofile-generate )
ENABLE_PGO_GENERATE=yes
;;
--pgo-use | --fprofile-use | -fprofile-use )
ENABLE_PGO_USE=yes
;;
-v | --verbose )
VERBOSE=yes
;;
-h | --help )
usage
exit 0
;;
-* )
echo "Bad usage"
usage
exit 1
;;
* )
TARGET=$1
;;
esac
shift
done
# Make sure the requested toolchain is the 1st in the path:
if [ "$BUILD_BITS" -eq 64 ]; then
export PATH="${TOOLCHAIN64_ROOT}/bin:${PATH}"
else
export PATH="${TOOLCHAIN32_ROOT}/bin:${PATH}"
fi
BUILD_DIR="${PRJ_ROOT}/${BUILD_DIR}${BUILD_BITS}-${BUILD_DIR_SUFFIX}"
if [ "ENABLE_PGO_GENERATE$ENABLE_PGO_USE" = "yesyes" ]; then
echo "Options --pgo-generate and --pgo-use cannot be used together."
exit 1
fi
if [ "$ENABLE_COVERAGE" = "yes" ]; then
BUILD_DIR+="+coverage"
fi
if [ "$ENABLE_PGO_GENERATE" = "yes" ]; then
BUILD_DIR+="+pgo_generate"
fi
if [ "$ENABLE_PGO_USE" = "yes" ]; then
BUILD_DIR+="+pgo_use"
fi
if [ "$ENABLE_PG" = "yes" ]; then
BUILD_DIR+="+prof"
fi
# Make sure the build dir exists and step into it:
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
if [ "$ENABLE_COVERAGE" = "yes" ]; then
CFLAGS="--coverage"
LDFLAGS="--coverage"
fi
if [ "$ENABLE_PG" = "yes" ]; then
CFLAGS="-pg -p -no-pie"
LDFLAGS="-pg -p -no-pie"
fi
if [ "$ENABLE_PGO_GENERATE" = "yes" ]; then
PROFILE_DATA_DIR="${BUILD_DIR}/data/pgo"
# Make sure we don't update some stale/outdated profile data corresponding
# to builds from old sources...
rm -rf "${PROFILE_DATA_DIR}"
CFLAGS="-fprofile-dir=${PROFILE_DATA_DIR} -fprofile-generate"
LDFLAGS="-fprofile-dir=${PROFILE_DATA_DIR} -fprofile-generate"
fi
if [ "$ENABLE_PGO_USE" = "yes" ]; then
SOURCE_PROFILE_DATA_DIR=`echo ${BUILD_DIR} | sed "s/pgo_use$/pgo_generate/"`"/data/pgo"
PROFILE_DATA_DIR="${BUILD_DIR}/data/pgo"
if [ ! -d "${SOURCE_PROFILE_DATA_DIR}" ]; then
echo "Cannot use --pgo-use before the project is built with --pgo-generate and"
echo "the profile data is collected in that build by running one or more executables."
exit 1
fi
# Copy PGO profile data from the corresponding pgo_generate build.
rm -rf "${PROFILE_DATA_DIR}"
mkdir -p "data"
cp -r "${SOURCE_PROFILE_DATA_DIR}" "${PROFILE_DATA_DIR}"
find "${PROFILE_DATA_DIR}" -name '*pgo_generate' | sed -e 'p;s/pgo_generate/pgo_use/' | xargs -n2 mv
CFLAGS="-fprofile-dir=${PROFILE_DATA_DIR} -fprofile-use=${PROFILE_DATA_DIR}"
LDFLAGS="-fprofile-dir=${PROFILE_DATA_DIR} -fprofile-use=${PROFILE_DATA_DIR}"
fi
printf "Building ${cyan}%s${endcolor} build in ${cyan}%s${endcolor}...\n" "$BUILD_TYPE" "$BUILD_DIR"
# Use Ninja if available as it's much faster than Make:
if which ninja >/dev/null 2>&1; then
GENERATOR_NAME="Ninja"
GENERATOR_FILE="rules.ninja"
BUILDER="ninja"
if [ "$VERBOSE" = "yes" ]; then
BUILDER_OPTIONS="-v"
fi
else
GENERATOR_NAME="Unix Makefiles"
GENERATOR_FILE="Makefile"
BUILDER="make"
if [ "$VERBOSE" = "yes" ]; then
BUILDER_OPTIONS="VERBOSE=1"
fi
fi
# Generate build files:
if [ ! -f "${GENERATOR_FILE}" ]; then
CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" \
cmake -G "${GENERATOR_NAME}" -DCMAKE_BUILD_TYPE="$BUILD_TYPE" "$PRJ_ROOT"
fi
if [ "$ENABLE_COVERAGE" = "yes" ]; then
printf "\n"
printf "1. Now you need to execute one (or more) executable(s) in order to generate\n"
printf " the profile data.\n"
printf "\n"
printf "2. Then you can study coverage of individual source files e.g. with command\n"
printf " ${green}$ gcov PATH_TO_SOURCE -o PATH_TO_OBJFILE${endcolor}\n"
printf " which creates a human readable *.gcov file.\n"
printf "\n"
fi
if [ "$ENABLE_PROFILE" = "yes" ]; then
printf "\n"
printf "1. Now you need to execute one (or more) executable(s) in order to generate\n"
printf " the profile data.\n"
printf "\n"
printf "2. Then you can create profiling report with this command:\n"
printf " ${green}$ gprof path/to/the/executable gmon.out > analysis.txt${endcolor}\n"
printf "\n"
fi
if [ "$ENABLE_PGO_GENERATE" = "yes" ]; then
printf "\n"
printf "1. Now you need to execute one (or more) executable(s) in order to collect\n"
printf " the profile data.\n"
printf "\n"
printf "2. When done, start the final build with the command\n"
printf " ${green}$ %s %s${endcolor}\n" "$0" `echo "$@" | sed 's/-generate/-use/g'`
printf "\n"
fi
if [ "$ENABLE_PGO_USE" = "yes" ]; then
# Clean to enforce complete rebuild with an up-to-date PGO profile.
"${BUILDER}" clean
fi
# The build:
"${BUILDER}" ${BUILDER_OPTIONS} ${TARGET}
STATUS=$?
exit $STATUS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment