{"id":1139,"date":"2026-06-02T16:08:59","date_gmt":"2026-06-02T15:08:59","guid":{"rendered":"https:\/\/www.martyndavis.com\/?p=1139"},"modified":"2026-06-02T16:08:59","modified_gmt":"2026-06-02T15:08:59","slug":"cmake-makefile","status":"publish","type":"post","link":"https:\/\/www.martyndavis.com\/?p=1139","title":{"rendered":"CMake Makefile"},"content":{"rendered":"CMake has plenty of advantages, I don&#8217;t need to re-iterate them here. However, if you&#8217;re like me, who lives on the command line and has <code>.\/configure<\/code> and <code>make<\/code> seemingly hard-wired into my fingers, it seems a bit cumbersome to have to type <code>mkdir build<\/code>, <code>cd build<\/code>, and (for example) <code>cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON<\/code> each time you want to build something.\r\n<br\/><br\/>\r\n\r\nIt got me thinking, and I haven&#8217;t come across anyone else suggesting it (I&#8217;m sure plenty of people have though)&#8230; why not drive CMake through a Makefile? Sure you can script it up easily enough, but it seems a bit single-purpose each time I do it, and my fingers are just itching to type `make`.\r\n<br\/><br\/>\r\n\r\nIt seems a weird concept, because of course CMake is, amongst other things, a Makefile generator. But here&#8217;s my take on it. I love being able to type <code>make<\/code> or <code>make release<\/code>:\r\n<br\/><br\/>\r\n\r\nMakefile:\r\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n# Usage:\r\n#   make          -&gt; debug build\r\n#   make release  -&gt; release build\r\n#   make test     -&gt; debug build + run tests\r\n#   make clean    -&gt; remove all build directories and symlink\r\n\r\nAPP_NAME := &lt;&lt;SET BINARY NAME HERE&gt;&gt;\r\n\r\nBUILD_DIR_DEBUG   := build\/Debug\r\nBUILD_DIR_RELEASE := build\/Release\r\n\r\nMAKEFLAGS += --no-print-directory\r\n\r\nCMAKE       := cmake\r\nCMAKE_FLAGS :=\r\nJOBS        := $(shell nproc 2&gt;\/dev\/null || sysctl -n hw.logicalcpu 2&gt;\/dev\/null || echo 4)\r\n\r\n.PHONY: all debug release test clean\r\n\r\n# -----------------------\r\n# Default target -&gt; debug\r\n# -----------------------\r\nall: debug\r\n\r\n# -----------\r\n# Debug build\r\n# -----------\r\ndebug: $(BUILD_DIR_DEBUG)\/Makefile\r\n\t$(CMAKE) --build $(BUILD_DIR_DEBUG) -- -j$(JOBS)\r\n\r\n$(BUILD_DIR_DEBUG)\/Makefile:\r\n\t@mkdir -p $(BUILD_DIR_DEBUG)\r\n\t$(CMAKE) -S . -B $(BUILD_DIR_DEBUG) -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Debug $(CMAKE_FLAGS)\r\n\r\n# -------------\r\n# Release build\r\n# -------------\r\nrelease: $(BUILD_DIR_RELEASE)\/Makefile\r\n\t$(CMAKE) --build $(BUILD_DIR_RELEASE) -- -j$(JOBS)\r\n\r\n$(BUILD_DIR_RELEASE)\/Makefile:\r\n\t@mkdir -p $(BUILD_DIR_RELEASE)\r\n\t$(CMAKE) -S . -B $(BUILD_DIR_RELEASE) -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Release $(CMAKE_FLAGS)\r\n\r\n# ------------------------\r\n# Test (debug build + run)\r\n# ------------------------\r\ntest: $(BUILD_DIR_DEBUG)\/Makefile\r\n\t$(CMAKE) --build $(BUILD_DIR_DEBUG) --target $(APP_NAME)_tests -- -j$(JOBS)\r\n\t@$(BUILD_DIR_DEBUG)\/$(APP_NAME)_tests --reporter console || true\r\n\r\n# -----\r\n# Clean\r\n# -----\r\nclean:\r\n\t@rm -rf build $(APP_NAME)\r\n\t@echo &quot;-&gt; build directories and symlink removed&quot;\r\n<\/pre><\/div>\r\n\r\n<br\/>\r\nSet APP_NAME to the same as your CMake <code>add_executable()<\/code> name. This assumes you have a unit test framework and compiles a test suite in &lt;APP_NAME&gt;_tests (runnable with <code>make test<\/code>). It also symlinks the normal binary into your main project directory for easy running from the command line. Verbose building is as simple as typing <code>VERBOSE=1 make<\/code>. Tweak as you see fit.\r\n","protected":false},"excerpt":{"rendered":"<p>CMake has plenty of advantages, I don&#8217;t need to re-iterate them here. However, if you&#8217;re like me, who lives on the command line and has .\/configure and make seemingly hard-wired into my fingers, it seems a bit cumbersome to have to type mkdir build, cd build, and (for example) cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON each time &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.martyndavis.com\/?p=1139\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;CMake Makefile&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/posts\/1139"}],"collection":[{"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1139"}],"version-history":[{"count":14,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/posts\/1139\/revisions"}],"predecessor-version":[{"id":1153,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=\/wp\/v2\/posts\/1139\/revisions\/1153"}],"wp:attachment":[{"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1139"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.martyndavis.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}