Last active
January 25, 2022 06:56
-
-
Save urraka/d3c290aceb19de69d6c6 to your computer and use it in GitHub Desktop.
makefile for building c/c++ projects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A comprehensive and easy to use C++ Makefile example can also be found here:
https://www.partow.net/programming/makefile/index.html