Skip to content

Instantly share code, notes, and snippets.

@urraka
Last active January 25, 2022 06:56
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 urraka/d3c290aceb19de69d6c6 to your computer and use it in GitHub Desktop.
Save urraka/d3c290aceb19de69d6c6 to your computer and use it in GitHub Desktop.
makefile for building c/c++ projects
# overridable variables:
# * project -> defaults to parent dir basename
# * src-root -> defaults to src
# * out-root -> defaults to bin
# * debug -> define it to anything for debug mode
# * platform -> possible values are win32,osx,linux,ios,iossim
# * inc, inc-* -> where * is the platform
# * lib, lib-* -> where * is the platform
# * flg, flg-* -> where * is the platform
# * ios.version -> installed sdk version
# * ios.version.min -> minimum sdk version
# * ios.plist -> file that will be copied as Info.plist
# * ios.bundle.files -> additional files to put along the executable file
project ?= $(notdir $(shell pwd))
src-root ?= src
out-root ?= bin
cxx ?= g++
cc ?= gcc
# try to detect platform if not given
ifndef platform
ifeq ($(shell uname | grep 'MINGW32_NT' -c),1)
platform ?= win32
endif
ifeq ($(shell uname | grep 'Linux' -c),1)
platform ?= linux
endif
ifeq ($(shell uname | grep 'Darwin' -c),1)
platform ?= osx
endif
endif # platform
# define extra stuff for ios
ifeq ($(platform),ios)
ios := true
ios.version.min ?= 4.0
ios.version ?= $(shell xcodebuild -showsdks | grep iphoneos | sed "s|.*iphoneos||")
ios.xcode.path := $(shell xcode-select --print-path)
ios.dev.path := $(ios.xcode.path)/Platforms/iPhoneOS.platform/Developer
ios.sdk.path := $(ios.dev.path)/SDKs/iPhoneOS$(ios.version).sdk
ios.arch := armv7
ios.flags := -isysroot $(ios.sdk.path) -arch $(ios.arch) -miphoneos-version-min=$(ios.version.min)
endif
ifeq ($(platform),iossim)
ios := true
ios.version.min ?= 4.0
ios.version ?= $(shell xcodebuild -showsdks | grep iphoneos | sed "s|.*iphoneos||")
ios.xcode.path := $(shell xcode-select --print-path)
ios.dev.path := $(ios.xcode.path)/Platforms/iPhoneSimulator.platform/Developer
ios.sdk.path := $(ios.dev.path)/SDKs/iPhoneSimulator$(ios.version).sdk
ios.arch := i386
ios.flags := -isysroot $(ios.sdk.path) -arch $(ios.arch) -mios-simulator-version-min=$(ios.version.min)
endif
ifdef ios
flg += $(ios.flags)
endif
# set the name of the output executable
binary-postfix-win32 := .exe
$(eval binary-postfix := $$(binary-postfix-$(platform)))
out-file := $(project)$(binary-postfix)
# define _DEBUG if compiling on debug mode and add some extra flags
ifdef debug
flg += -g -D_DEBUG -Wall
out-dir := $(out-root)/$(platform)-d
else
flg += -g -O3 -Wall
out-dir := $(out-root)/$(platform)
endif
# consolidate all the platform specific options into inc,lib,flg variables
$(eval inc += $$(inc-$(platform)))
$(eval lib += $$(lib-$(platform)))
$(eval flg += $$(flg-$(platform)))
# find all directories under src-root and set matching directories for dependency and object files
src-dirs := $(shell find $(src-root) -depth -type d)
obj-dirs := $(patsubst $(src-root)%,$(out-dir)/obj%,$(src-dirs))
dep-dirs := $(patsubst $(src-root)%,$(out-dir)/dep%,$(src-dirs))
# make a list of directories to be created
out-dirs := $(out-dir)/ $(obj-dirs) $(dep-dirs)
ifdef ios
out-dirs += $(out-dir)/bundle
out-dirs += $(out-dir)/bundle/Documents
out-dirs += $(out-dir)/bundle/Library
out-dirs += $(out-dir)/bundle/tmp
out-dirs += $(out-dir)/bundle/$(project).app
endif
# define extensions used for c++
cxx-ext := cpp cxx cc
ifdef ios
cxx-ext += m mm
endif
# find all source files under the source directories and make matching lists of dependency and objects files
src-cxx := $(foreach d,$(src-dirs),$(foreach e,$(cxx-ext),$(wildcard $d/*.$e)))
src-cc := $(foreach d,$(src-dirs),$(wildcard $d/*.c))
obj-cxx := $(patsubst $(src-root)%,$(out-dir)/obj%.o,$(src-cxx))
obj-cc := $(patsubst $(src-root)%,$(out-dir)/obj%.o,$(src-cc))
dep-cxx := $(patsubst $(src-root)%,$(out-dir)/dep%.d,$(src-cxx))
dep-cc := $(patsubst $(src-root)%,$(out-dir)/dep%.d,$(src-cc))
# main target on ios is ios.bundle
ifdef ios
out-file := bundle/$(project).app/$(out-file)
ios.outdir := $(out-dir)/bundle/$(project).app
ios.bundle: $(out-dir)/$(out-file) $(ios.outdir)/Info.plist \
$(addprefix $(ios.outdir)/,$(ios.bundle.files)) | $(out-dirs)
.PHONY: ios.bundle
ifdef ios.plist
$(ios.outdir)/Info.plist: $(ios.plist)
cp -f $(ios.plist) $@
else
doctype := <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" \
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
$(ios.outdir)/Info.plist: makefile
@echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
@echo '$(doctype)' >> $@
@echo '<plist version="1.0">' >> $@
@echo ' <dict>' >> $@
@echo ' <key>CFBundleDevelopmentRegion</key>' >> $@
@echo ' <string>English</string>' >> $@
@echo ' <key>CFBundleDisplayName</key>' >> $@
@echo ' <string>$(project)</string>' >> $@
@echo ' <key>CFBundleExecutable</key>' >> $@
@echo ' <string>$(project)</string>' >> $@
@echo ' <key>CFBundleIconFile</key>' >> $@
@echo ' <string>Icon.png</string>' >> $@
@echo ' <key>CFBundleIdentifier</key>' >> $@
@echo ' <string>com.example.$(project)</string>' >> $@
@echo ' <key>CFBundleInfoDictionaryVersion</key>' >> $@
@echo ' <string>6.0</string>' >> $@
@echo ' <key>CFBundleName</key>' >> $@
@echo ' <string>$(project)</string>' >> $@
@echo ' <key>CFBundlePackageType</key>' >> $@
@echo ' <string>APPL</string>' >> $@
@echo ' <key>CFBundleSignature</key>' >> $@
@echo ' <string>????</string>' >> $@
@echo ' <key>CFBundleShortVersionString</key>' >> $@
@echo ' <string>1.0</string>' >> $@
@echo ' <key>CFBundleVersion</key>' >> $@
@echo ' <string>1.0.0</string>' >> $@
@echo ' <key>UIStatusBarStyle</key>' >> $@
@echo ' <string>UIStatusBarStyleBlackOpaque</string>' >> $@
@echo ' <key>LSRequiresIPhoneOS</key>' >> $@
@echo ' <true/>' >> $@
@echo ' </dict>' >> $@
@echo '</plist>' >> $@
endif # ios.plist
define ios.copyfiles
$(ios.outdir)/$1: $1
cp -rf "$1" "$(ios.outdir)/"
endef
$(foreach f,$(ios.bundle.files),$(eval $(call ios.copyfiles,$f)))
endif # ios
# target for main executable
$(out-dir)/$(out-file): $(obj-cxx) $(obj-cc) | $(out-dirs)
@echo "Linking $@..."
@$(cxx) -o $@ $(obj-cxx) $(obj-cc) $(lib) $(flg)
# create required directories
$(out-dirs):
@mkdir -p $@
# make-obj(compiler,source)
define make-obj
$(out-dir)/obj/$2.o: $(src-root)/$2 | $(out-dirs)
@echo "Compiling $$<..."
@$1 -o $$@ -MF"$(out-dir)/dep/$2.d" -MMD -MP \
-MT"$(out-dir)/dep/$2.d $(out-dir)/obj/$2.o" $(inc) $(flg) -c $$<
endef
# create a rule for each source file using make-obj template
$(foreach s,$(src-cxx),$(eval $(call make-obj,$(cxx),$(patsubst $(src-root)/%,%,$s))))
$(foreach s,$(src-cc),$(eval $(call make-obj,$(cc),$(patsubst $(src-root)/%,%,$s))))
# include generated dependency files if they exist
ifneq ($(MAKECMDGOALS),clean)
-include $(dep-cxx) $(dep-cc)
endif
# cleanup
clean:
@rm -rf $(out-root)
.PHONY: clean
@ArashPartow
Copy link

A comprehensive and easy to use C++ Makefile example can also be found here:

https://www.partow.net/programming/makefile/index.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment