------------------------------------------------------------------------------- make and Makefile More info https://learnxinyminutes.com/docs/make/ ------------------------------------------------------------------------------- Make options -k Continue other dependancies after an error (don't stop) -t touch all so that everything is actually ok you can then touch just the sources than need to be re-done ------------------------------------------------------------------------------- Shell scripts in Makefile You must backslash all lines to make them a single line, also hiding the resultact command is usful. FILE_TEST: @echo "performing shell commands" @if [ -f file ]; \ then set -x; \ command args... \ fi OR .ENV_CHECK: @if [ $(PMISSOURCE) != "/p7" ]; then exit 1; fi OR .ENV_CHECK: @[ $(PMISSOURCE) != "/p7" && exit 1 ------------------------------------------------------------------------------- Full ANSI/OSI error checking with gcc CC = gcc CFLAGS = -Wall -Wpointer-arith -Wconversion \ -Wstrict-prototypes -Wmissing-prototypes \ -ansi -pedantic -g -O \ #-DDEBUG ------------------------------------------------------------------------------- Selective recompile after a header edit EG: edit a header, but only one C file is actually effected make -t touch module_to_recompile make ------------------------------------------------------------------------------- Automatic Variables & Wildcards process: file*.txt #using a wildcard to match filenames @echo $^ # $^ is a variable containing the list of prerequisites @echo $@ # the target name #(for multiple target rules, $@ is whichever caused the rule to run) @echo $< # the first prerequisite listed @echo $? # only the dependencies that are out of date @echo $+ # all dependencies including duplicates (unlike normal) #@echo $| # all of the 'order only' prerequisites # Even if we split up the rule dependency definitions, $^ will find them process: ex1.txt file0.txt # ex1.txt will be found, but file0.txt will be deduplicated, and removed ------------------------------------------------------------------------------- Suffix manipluation substitution references http://www.gnu.org/software/make/manual/make.html#Substitution-Refs # Remove suffix - compile '.c' files individually FILES_IN = file1.c file2.c FILES_OUT = $(FILES_IN:.c=) # result = file1 file2 # Append suffix -- add .bak to all files FILES_IN = file1 file2.c FILES_OUT = $(FILES_IN:=.bak) # result = file1.bak file2.c.bak # replace specific suffixes (intermedite vars needed) FILES_IN = file1.doc file2.xls FILES_O_1 = $(FILES_IN:.doc=.docx) FILES_OUT = $(FILES_O_1:.xls=.xlsx) # result = file1.docx file2.xlsx # GNU make pattern replacement - more verbose but can be chained FILES_OUT = $(patsubst %.xls,%.xlsx,$(patsubst %.doc,%.docx,$(FILES_IN))) name ?= Jean # name only set if environment var 'name' is not defined override name = David # command line arg cannot override name += grey # append to the variable list # ways of using a variable echo: @echo $(name) @echo ${name} @echo $$name # shell variable not make variable ------------------------------------------------------------------------------- Install all ".sh" suffixes BINDIR=/usr/local/bin SCRIPT_SOURCE=$(wildcard *.sh) SCRIPT_FILES=$(SCRIPT_SOURCE:.sh=) INSTALL_FILES=$(addprefix $(BINDIR)/, $(SCRIPT_FILES)) install: ${INSTALL_FILES} test: $(BINDIR)/contained_ssh_test .PHONY: install test # Install scripts from source with '.sh' suffix $(BINDIR)/%: %.sh install -m 755 $< $@ ---------------- simularly for .c and .o using patsubst SOURCE = $(wildcard *.c */*.c) OBJECT = $(patsubst %.c,%.o,$(sourcefiles)) ls: * src/* @echo $(filter %.txt, $^) @echo $(notdir $^) @echo $(join $(dir $^),$(notdir $^)) ------------------------------------------------------------------------------- Directives (conditonal macros) # Include other makefiles, useful for platform specific code include foo.mk # Conditional compilation sport = tennis report: ifeq ($(sport),tennis) @echo 'game, set, match' else @echo "They think it's all over; it is now" endif # There are also ifneq, ifdef, ifndef foo = true ifdef $(foo) bar = 'hello' endif ------------------------------------------------------------------------------- Adding a new make rule Convert .c into a .cgi program # This is a older suffix style rule (sort of obsolete) .c.cgi: $(CC) $(CFLAGS) -o $@ $< $(LIBS) chmod 755 $@ .SUFFIXES: .SUFFIXES: .cgi $(SUFFIXES) # this is a newer style %.cgi: %c $(CC) $(CFLAGS) -o $@ $< $(LIBS) chmod 755 $@ %.cgi: %o $(CC) $(CFLAGS) -o $@ $< $(LIBS) chmod 755 $@ The last such rule overrides older ones But the first rule defined will be used if posible. As such in the above, if both file.c and file.o exist, then '.c' file (first one) will be used to generate a file.cgi Use .PHONY to specify make targets that are not actually generated That is the rule should always be run when called. And anything dependant on a PHONY rule also always run .PHONY: all all: $(TARGETS) install: all ------------------------------------------------------------------------------- Variables from external source WARNING: Do NOT uset `...` in make files unless part of a shell command! This appears to work but will fail... OS = `uname` all: @echo "OS=$(OS)" which when you run "make returns: hostname=Linux But that is NOT CORRECT, and using directives will fail OS=`uname` all: ifeq ($(OS),Linux) @echo "true" else @echo "false" endif Will returns: false That is because the make variable is actually "`uname`" which the shell expanded, NOT the make command OS = `uname` all: @echo 'OS=$(OS)' which returns: OS=`uname` Correct solution is OS = $(shell uname) all: @echo 'OS=$(OS)' Which returns, OS=Linux ------------------------------------------------------------------------------- Help rule.. This prints out ## located on same line as the rule all: $(TARGETS) ## make all targets help: ## print helplines like this @awk -F ':.*##' '$$0 ~ FS \ {printf "%15s%s\n", $$1 ":", $$2}' \ $(MAKEFILE_LIST) | sort .PHONY: all help ------------------------------------------------------------------------------- Substitutions Rules for template files # Sed substitions rule # (uses '|' as as to avoid conflict with '/') .sed: @echo "SED SUBSTITUTIONS \"$@.sed\" ==> \"$@\"" @sed -e 's|===BROWSERDIR===|$(BROWSERDIR)|' \ -e 's|===LIBRARY_RC===|$(LIBRARY_RC)|' \ -e 's|===USERS_RC===|$(USERS_RC)|' \ -e 's|===X_BITMAPS===|$(X_BITMAPS)|' \ -e '/===COLOR_SETTINGS_DIVIDER===/d' \ < $@.sed >$@ .SUFFIXES: .sed Some of the special character around the substition strings are as follows so take your pick. ===LABEL=== @@LABEL@@ @#LABEL#@ An alternative templating method is "envsubst" ------------------------------------------------------------------------------- Hide intermediate object files in a sub-directory This is for large programs # Example: binaries in directory above, # while objects in sub-directory (or $HOME/.ccache) BINDIR = ../bin OBJDIR = obj TARGET = $(BINDIR)/foo OBJS = OBJS += $(OBJDIR)/ack.o OBJS += $(OBJDIR)/bar.o OBJS += $(OBJDIR)/frotz.o OBJS += $(OBJDIR)/ozmoo.o $(TARGET): $(OBJS) @echo "Linking $@" @$(LINK.c) -o $@ $? $(OBJDIR)/%.o: %.c @echo "Compiling $<" @$(COMPILE.c) -o $@ $< Of course, this is oversimplified. However, it should give you the idea. The LINK.c and COMPILE.c are predefined macros supplied with the version of make that comes with SunOS, and all they are is predefined 'cc' commands. 'man make' for more info about predefined macros on your system. The above example ensures that your source, executable, and objects are separate. Of course, depending on how large the project is, you could make an inc/ directory also to separate your include files. Of course you would have to define that dir in your makefile as well. ------------------------------------------------------------------------------- Auto-increment of a build version number Makefile.autoincr.gen : Integers only! VERSION = 23 NEWVERS = `expr $(VERSION) + $(UDVAL)` update: @mv Makefile Makefile.bak @sed "s/^VERSION[ \t]*=[ \t]*[0-9]*/VERSION = $(NEWVERS)/" \ Makefile.bak > Makefile Makefile.autoincr.sun : Bells & whistles! VERSION = 1.23 NEWVERS = echo "scale=2 ; $(VERSION) + $(UDVAL)" | bc update: @mv Makefile Makefile.bak @sed "s/^VERSION[ \t]*=[ \t]*[0-9.]*/VERSION = $(NEWVERS:sh)/" \ Makefile.bak > Makefile Then the following can be used to update the version number make -f Makefile.autoincr.sun update UDVAL="0.01" Another way is to have the version in a separate file and increment this. -------------------------------------------------------------------------------