Skip to content

Instantly share code, notes, and snippets.

@brianonn
Last active June 25, 2025 23:29
Show Gist options
  • Save brianonn/f10eaf000a4358478afb5b3cc8925b1d to your computer and use it in GitHub Desktop.
Save brianonn/f10eaf000a4358478afb5b3cc8925b1d to your computer and use it in GitHub Desktop.
a single Makefile bootstrap for a simple C++ project with cmake

a pre-make for CMake based projects

This is my simple pre-make script for bootstrapping CMake for a small project

Usage

Make a new cpp project directory and download this Makefile to your new project workspace via curl using the following commands :

$ mkdir my-new-project
$ cd my-new-project
$ curl -sSL \
   -o Makefile \
   --no-clobber \
   https://gist.githubusercontent.com/brianonn/f10eaf000a4358478afb5b3cc8925b1d/raw/Makefile
$ make

When building with the Makefile it will create the executable in build/main.

I have the following alias defined as well. This will add a new alias cppboot and make it easier to just invoke cppboot in a new directory whenever you want to bootstrap a new cpp project on Linux:

alias cppboot='[ ! -e Makefile ] && curl -sSL -o Makefile https://gist.githubusercontent.com/brianonn/f10eaf000a4358478afb5b3cc8925b1d/raw/Makefile  || echo "Makefile already exists in the current directory"'

After bootstrapping with the Makefile you can remove the Makefile and replace it with build.sh for a more advanced build environment with Debug and Release and Test support. The file build.sh file is not always needed, but can be helpful for these more complex builds. Download build.sh with curl too, if you want to use it.

$ rm -rf build
$ rm -rf Makefile
$ curl -sSL \
   -o build.sh \
   --no-clobber \
   https://gist.githubusercontent.com/brianonn/f10eaf000a4358478afb5b3cc8925b1d/raw/build.sh
$ chmod 755 build.sh
$ ./build.sh

You can get help with ./build.sh -h

When building with build.sh it will create the executables in build/Debug/main and build/Release/main

build/
├── Debug
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── main
│   └── Makefile
├── Release
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── main
│   └── Makefile
└── _THIS_DIR_CAN_BE_REMOVED_

See also

premake5 - A more comprehensive and cross-platform/cross-architecture pre-make system.

#!/bin/bash
## This build.sh file is also helpful
cmake_build_dir="build"
RM="/bin/rm -rf"
# files generated by cmake
cmake_generated=()
cmake_generated+=("${cmake_build_dir}")
cmake_generated+=(Makefile generated)
cmake_generated+=(CMakeCache.txt *.cmake DartConfiguration.tcl Testing generated)
# create a gitignore file in the directory passed in $1
function gitignore() {
local gitignore="$1/.gitignore"
if [[ ! -f ${gitignore} ]]; then
(
echo "# Autogenerated by build.sh"
echo "#"
echo "# ignore this directory and eveything inside it"
echo "*"
) >> ${gitignore}
fi
}
# first turn long args into short args for getopts to use
args=()
while [ $# -gt 0 ]; do
case "$1" in
--clean) args+=(-c) ;;
--verbose) args+=(-v) ;;
--test) args+=(-t) ;;
--help) args+=(-h) ;;
--) args+=("$@") ;;
*) args+=("$1") ;;
esac
shift
done
# set the new args
set -- "${args[@]}"
# now use getopts which doesn't support long args
testing=0
clean=0
verbose=""
while getopts "hcvt" OPT; do
case "$OPT" in
t) testing=1 ;;
c) clean=1
echo "Cleaning ${cmake_build_dir} dir ..."
echo ${RM} "${cmake_generated[@]}"
${RM} "${cmake_generated[@]}"
exit 0
;;
v) verbose="-v" ;;
h) echo "Usage: build.sh [-h | --help] [-v | --verbose] [-t | --test] [ -c | --clean]"
exit 0
;;
\?) exit 1
;;
esac
done
mkdir -p ${cmake_build_dir}
touch ${cmake_build_dir}/_THIS_DIR_CAN_BE_REMOVED_
gitignore ${cmake_build_dir}
cmake -S . -B ${cmake_build_dir}/Debug -D CMAKE_BUILD_TYPE=Debug
cmake --build ${cmake_build_dir}/Debug $verbose
cmake -S . -B ${cmake_build_dir}/Release -D CMAKE_BUILD_TYPE=Release
cmake --build ${cmake_build_dir}/Release $verbose
if [ $testing -eq 1 ]; then
for config in Debug Release; do
echo
echo "--- TEST::BEGIN (${config}) ---"
echo
ctest --test-dir ${cmake_build_dir}/${config}/
echo
echo "--- TEST::END ---"
echo
done
fi
PROJECT = main
MAINCPP = main.cpp
CMAKELISTS = CMakeLists.txt
.PHONY: clean run bootstrap all
all: build/$(PROJECT)
build/$(PROJECT): $(MAINCPP) build
$(MAKE) -C build
build: $(CMAKELISTS)
@ rm -rf build
cmake -B build
run: build/$(PROJECT)
build/$(PROJECT)
clean:
rm -rf build
$(MAINCPP) $(CMAKELISTS):
rm -f $(CMAKELISTS) $(MAINCPP)
@ echo "cmake_minimum_required(VERSION 3.14)" >> $(CMAKELISTS)
@ echo "project(main)" >> $(CMAKELISTS)
@ echo "set(CMAKE_CXX_STANDARD 17)" >> $(CMAKELISTS)
@ echo "add_executable(main main.cpp)" >> $(CMAKELISTS)
@ echo "#include <iostream>" >> $(MAINCPP)
@ echo "" >> $(MAINCPP)
@ echo "int main(void) {" >> $(MAINCPP)
@ echo " std::cout << \"Running main()...\" << std::endl;" >> $(MAINCPP)
@ echo " // add your code here" >> $(MAINCPP)
@ echo " std::cout << \"Done!\" << std::endl;" >> $(MAINCPP)
@ echo " return 0;" >> $(MAINCPP)
@ echo "}" >> $(MAINCPP)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment