Like any developer, I like to use the right tool for the job – when it comes to coding projects, the choice of language can greatly impact the performance and workflow of the project in the future. For quick CLI’s and web projects, I like to use Python; for short single-use and domain-specific scripts, shell is nice; for native concurrency primitives and a simple language, I use Golang. Many languages that are catching the world by a storm, like Golang, Rust, Kotlin, Crystal, and Nim have built-in or otherwise generally accepted build systems that are relatively effortless to leverage in making easily buildable and installable packages. However, as a student in Electrical Engineering who frequently drops down to lower level languages like C and C++, project build systems pose a larger barrier to entry. To solve this problem, I have a relatively simple “universal” Makefile for C and C++ (and mixed), which can be easily adaptable for many projects.
I have also experimented with CMake as a meta-build system. CMake and utilities like it do have their advantages – they make cross-compilation easier, they can be used with many different build systems (even at the same time) like XCode, Make, Ninja, etc. However, I’ve found CMake to be overkill for small projects with few (0-5) executable targets. This Makefile plays more nicely with version control (fewer things to put in .gitignore), and is a lower cognitive load to understand – a review of implicit Make rules covers pretty much everything.
TARGET_EXEC = main
In writing this Makefile, I pulled ideas heavily from the following projects:
My version adds CXXFLAGS and CFLAGS, as well as a debugger switch for GCC to compile with debugging symbols. It defines two targets, for a main binary and a unittest binary which could be linked against a testing library of your choice.