Make
Makefiles are automation built around shellscripts,
they are normally used to codify build instructions, but can be used for anything.
Documentation
Gnu Makefile conventions https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html Text Function Reference (ex: ($shell ..) $(patsubst ..)
)https://www.gnu.org/software/make/manual/html_node/Text-Functions.html Automatic Variables (ex: $@, $<
)https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables
Tutorials
university of maryland tutorial http://www.cs.umd.edu/class/fall2002/cmsc214/Tutorial/makefile.html mrbook tutorial http://mrbook.org/blog/tutorials/make/
Notes
make usage make basics make examples
Usage
make ## compile make -d v ## additional debug information make clean ## delete all .o files and program
Core Concepts
At it's core, a makefile is responsible for doing 3x things:
- configuring compiler to use, and compiler-flags
- defining program to compile, and all the object-files it is dependent on
- defining each object-file, it's cpp/header files, and all other dependent header files
Examples
The following examples all make reference to a sourcetree that looks like the following.
src/ libhello.h libhello.c main.c # includes libhello.hEach example below is a little cleaner than the last, but a little more abstract.
ex 1: compile everything, every time
This will get the job done, but will recompile every file every time. Not very efficient.
# ============= # configuration # ============= # compiler CC = gcc # compiler DEBUG = -g # compiler's debug flag (so can use gdb) STD = c99 # the C-standard to compile with CFLAGS = -Wall $(DEBUG) # compile-executable flags LFLAGS = -Wall -c $(DEBUG) # compile-object (.o) flags # ============= # build targets # ============= # default/implied 'make' argument # # target: depends1 depends2 ... bin/main: test -d bin || mkdir bin $(CC) -std=$(STD) $(CFLAGS) build/main.c build/libhello.c -o bin/main clean: test -d bin && rm -rf binex 2: Compile .o for each file, then combine
This is much more efficent. Every file gets compiled to .o (only as-needed), then those .o files are built into your final executable.
My chief complaint here is that this makefile is going to require an exceptional amount of maintenance, defining every single .o file.
# ============= # configuration # ============= # compiler CC = gcc # compiler DEBUG = -g # compiler's debug flag (so can use gdb) STD = c99 # the C-standard to compile with CFLAGS = -Wall $(DEBUG) # compile-executable flags LFLAGS = -Wall -c $(DEBUG) # compile-object (.o) flags # ============= # build targets # ============= # default/implied 'make' argument # # target: depends1 depends2 ... bin/main: build/main.o build/libhello.o test -d bin || mkdir bin $(CC) -std=$(STD) $(CFLAGS) build/main.o build/libhello.o -o bin/main # other possible 'make' arguments # ex: make clean # ex: make build/main.o build/main.o: test -d build || mkdir build $(CC) -std=$(STD) $(LFLAGS) src/main.c -o $@ build/libhello.o: test -d build || mkdir build $(CC) -std=$(STD) $(LFLAGS) src/libhello.c -o $@ clean: test -d build && rm -rf build test -d bin && rm -rf bin
ex 3: low-maintenance makefile
This produces exactly the same result as the 2nd-example, but it performs additional automation within the makefile.
- sourcfiles are found automatically
- .o file definitions and dependencies are produced automatically
# ============= # configuration # ============= # compiler CC = gcc # compiler DEBUG = -g # compiler's debug flag (so can use gdb) STD = c99 # the C-standard to compile with CFLAGS = -Wall $(DEBUG) # compile-executable flags LFLAGS = -Wall -c $(DEBUG) # compile-object (.o) flags # targets SRCS = $(shell find src/ -type f -name '*.c') OBJS = $(patsubst src/%,build/%,$(SRCS:.c=.o)) # ============= # build targets # ============= # default/implied 'make' argument # # target: depends1 depends2 ... bin/main: $(OBJS) test -d bin || mkdir bin @echo $(OBJS) $(CC) -std=$(STD) $(CFLAGS) $(OBJS) -o bin/main ## IMPORTANT: if this is cpp, before $(OBJS) you must include the target's .cpp file # .o-target for each .c-file build/%.o: src/%.c test -d build || mkdir build $(CC) -std=$(STD) $(LFLAGS) -o $@ $< # other 'make' arguments clean: test -d build && rm -rf build test -d bin && rm -rf bin
$@
is the file being generated$<
name of first prerequisiteSee https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables
makefile
Compiler
The makefile itself seems quite flexible with how you use it, but there seems to be an agreed-upon standard for what variables refer to what. Here are some of the more common variables:
PROG= ## name of output executable OBJS= ## name of all *.o files to be included (?) CC= ## compiler name DEBUG= ## compiler flags for debug-mode (allow use of gdb) CFLAGS= ## compiler flags for *.o object-file compilation LFLAGS= ## compiler flags to determine libraries (?)files/dependencies
The majority of your makefile defines files to be compiled, and their dependencies. You can define as many executables or *.o as you like, but by default only the first program (and all
- .o files it is dependent on) are compiled.
Movie.o: Movie.cpp Movie.h OtherModule.h g++ -Wall -c Movie.cpp
Other
make builtins
gnu-make has a subset of commands that you can use within your makefile.
See excellent documentation: https://www.gnu.org/software/make/manual/html_node/Text-Functions.html .
@command # run cli-command $(shell command) # assign output of cli-command to var $(subst from,to,text) $(patsubst %.c,%.o,file1.c file2.c file3.c) # '%' is a wildcard match. '%'s value is preserved in replacement $(var:pattern=replacement) # single variable pattern-replacementcreate directory
Occasionally, it makes sense to specify the creation of directories in your makefile. (such as creating a /bin directory that can be put in .gitignore). You can do this with the following snippet:
BINDIR = bin dummy_build_folder := $(shell mkdir -p $(BINDIR))
echo variable
Sometimes it's handy to try to figure out just what exactly your compiler is running. You can do this. using a prefix of
@
runs a shell command.all: @clear @echo "_INCL: $(_INCL)" @echo "_SRCDIR: $(_SRCDIR)"
Sources
http://www.cs.umd.edu/class/fall2002/cmsc214/Tutorial/makefile.html http://mrbook.org/blog/tutorials/make/