targets: prerequisites
command
command
command
blah: blah.o
cc blah.o -o blah
blah.o: blah.c
cc -c blah.c -o blah.o
blah.c:
echo "int main() {return 0;}" > blah.c
Lims-iMac:42 YCLim$ make blah
echo "int main() {return 0;}" > blah.c
cc -c blah.c -o blah.o
cc blah.o -o blah
Lims-iMac:42 YCLim$ ls -la
total 40
drwxr-xr-x 6 YCLim staff 204 Jun 3 14:20 .
drwx------@ 6 YCLim staff 204 Jun 3 13:56 ..
-rw-r--r-- 1 YCLim staff 119 Jun 3 14:20 Makefile
-rwxr-xr-x 1 YCLim staff 4248 Jun 3 14:20 blah
-rw-r--r-- 1 YCLim staff 23 Jun 3 14:20 blah.c
-rw-r--r-- 1 YCLim staff 608 Jun 3 14:20 blah.o
Lims-iMac:42 YCLim$ make blah
make: `blah' is up to date.
blah: blah.o
cc blah.o -o blah
blah.o: blah.c
cc -c blah.c -o blah.o
blah.c:
echo "int main() {return 0;}" > blah.c
clean:
rm -f blah blah.o blah.c
Lims-iMac:42 YCLim$ make clean
rm -f blah blah.o blah.c
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 3 14:33 .
drwx------@ 6 YCLim staff 204 Jun 3 13:56 ..
-rw-r--r-- 1 YCLim staff 152 Jun 3 14:32 Makefile
This will always run both targets, because some_file
depends on other_file, which is never created.
some_file: other_file
touch some_file
other_file:
echo "nothing"
Lims-iMac:42 YCLim$ make somefile
make: *** No rule to make target `somefile'. Stop.
files := file1 file2
some_file: $(files)
echo "Look at this variable: " $(files)
touch some_file
file1:
touch file1
file2:
touch file2
clean:
rm -f file1 file2 some_file
Lims-iMac:42 YCLim$ make some_file
touch file1
touch file2
echo "Look at this variable: " file1 file2
Look at this variable: file1 file2
touch some_file
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 6 YCLim staff 204 Jun 3 14:46 .
drwx------@ 6 YCLim staff 204 Jun 3 14:46 ..
-rw-r--r-- 1 YCLim staff 176 Jun 3 14:46 Makefile
-rw-r--r-- 1 YCLim staff 0 Jun 3 14:46 file1
-rw-r--r-- 1 YCLim staff 0 Jun 3 14:46 file2
-rw-r--r-- 1 YCLim staff 0 Jun 3 14:46 some_file
Lims-iMac:42 YCLim$ make clean
rm -f file1 file2 some_file
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 3 14:47 .
drwx------@ 6 YCLim staff 204 Jun 3 14:46 ..
-rw-r--r-- 1 YCLim staff 176 Jun 3 14:46 Makefile
x := dude
all:
echo $(x)
echo ${x}
# Bad practice, BUT WORKS
echo $x
Lims-iMac:42 YCLim$ make
echo dude
dude
echo dude
dude
# Bad practice, BUT WORKS
echo dude
dude
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 3 14:51 .
drwx------@ 6 YCLim staff 204 Jun 3 14:47 ..
-rw-r--r-- 1 YCLim staff 75 Jun 3 14:51 Makefile
all: one two three
one:
touch one
two:
touch two
three:
touch three
clean:
rm -f one two three
Lims-iMac:42 YCLim$ make
touch one
touch two
touch three
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 6 YCLim staff 204 Jun 3 14:53 .
drwx------@ 6 YCLim staff 204 Jun 3 14:47 ..
-rw-r--r-- 1 YCLim staff 101 Jun 3 14:53 Makefile
-rw-r--r-- 1 YCLim staff 0 Jun 3 14:53 one
-rw-r--r-- 1 YCLim staff 0 Jun 3 14:53 three
-rw-r--r-- 1 YCLim staff 0 Jun 3 14:53 two
Lims-iMac:42 YCLim$ make clean
rm -f one two three
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 3 14:53 .
drwx------@ 6 YCLim staff 204 Jun 3 14:47 ..
-rw-r--r-- 1 YCLim staff 101 Jun 3 14:53 Makefile
all: f1.o f2.o
f1.o f2.o:
echo $@
Lims-iMac:42 YCLim$ make
echo f1.o
f1.o
echo f2.o
f2.o
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 3 14:58 .
drwx------@ 6 YCLim staff 204 Jun 3 14:47 ..
-rw-r--r-- 1 YCLim staff 36 Jun 3 14:58 Makefile
- Both
*
and%
are called wildcards in Make, but they mean entirely different things. *
searches your filesystem for matching filenames.- I suggest that you always wrap it in the wildcard function, because otherwise you may fall into a common pitfall described below
print: $(wildcard *.c)
ls -la
Lims-iMac:42 YCLim$ make print
ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 3 15:44 .
drwx------@ 6 YCLim staff 204 Jun 3 14:47 ..
-rw-r--r-- 1 YCLim staff 80 Jun 3 15:44 Makefile
*
may be used in the target, prerequisites, or in the wildcard
function.
Danger: *
may not be directly used in a variable definitions
Danger: When *
matches no files, it is left as it is (unless run in the wildcard
function)
thing_wrong := *.o # Don't do this '*' will not get expanded
thing_right := $(wildcard *o)
all: one two three four
# Fails, becaue $(thing_wrong) = "*.o"
one: $(thing_wrong)
# Stays as *.o if there are no files that match this pattern
two: *.o
# WORKS as you expect! In this case, it does nothing!
three: $(thing_right)
# Same as rule three
four: $(wildcard *.o)
Lims-iMac:42 YCLim$ make
make: *** No rule to make target `*.o', needed by `one'. Stop.
Lims-iMac:42 YCLim$ make three
make: Nothing to be done for `three'.
Lims-iMac:42 YCLim$ make thing_right
make: *** No rule to make target `thing_right'. Stop.
Lims-iMac:42 YCLim$ make four
make: Nothing to be done for `four'.
%
is really useful, but is somewhat confusing because of the variety of situations it can be used in.
- When used in "matching" mode, it matches one or more characters in a string. This match is called the stem.
- When used in "replacing" mode, it takes the stem that was matched and replaces that in a string.
%
is most often used in rule definitions and in some specific functions.
See these sections on examples of it being used:
- Static Pattern Rules
- Pattern Rules
- String Substitution
- The vpath Directive
There are many automatic variables, but often only a few show up:
hey: one two
# Outputs "hey", since this is the first target
echo $@
# Outputs all prerequisities newr than the target
echo $?
# Outputs all prerequisites
echo $^
touch hey
one:
touch one
two:
touch two
clean:
rm -f hey one two
Lims-iMac:42 YCLim$ make hey
touch one
touch two
# Outputs "hey", since this is the first target
echo hey
hey
# Outputs all prerequisities newr than the target
echo one two
one two
# Outputs all prerequisites
echo one two
one two
touch hey
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 6 YCLim staff 204 Jun 5 12:53 .
drwx------@ 7 YCLim staff 238 Jun 4 14:21 ..
-rw-r--r-- 1 YCLim staff 244 Jun 5 12:53 Makefile
-rw-r--r-- 1 YCLim staff 0 Jun 5 12:53 hey
-rw-r--r-- 1 YCLim staff 0 Jun 5 12:53 one
-rw-r--r-- 1 YCLim staff 0 Jun 5 12:53 two
Lims-iMac:42 YCLim$ make clean
rm -f hey one two
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 5 12:55 .
drwx------@ 7 YCLim staff 238 Jun 4 14:21 ..
-rw-r--r-- 1 YCLim staff 244 Jun 5 12:53 Makefile
Make loves c compilation. And every time it expresses its love, things get confusing. Perhaps the most confusing part of Make is the magic/automatic rules that are made. Make calls these "implicit" rules. I don't personally agree with this design decision, and I don't recommend using them, but they're often used and are thus useful to know. Here's a list of implicit rules:
- Compiling a C program:
n.o
is made automatically fromn.c
with a command of the form$(CC) -c $(CPPFLAGS) $(CFLAGS)
- Compiling a C++ program:
n.o
is made automatically fromn.cc
orn.cpp
with a command of the form$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)
- Linking a single object file:
n
is made automatically fromn.o
by running the command$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
The important variables used by implicit rules are:
CC
: Program for compiling C programs; default ccCXX
: Program for compiling C++ programs; default g++CFLAGS
: Extra flags to give to the C compilerCXXFLAGS
: Extra flags to give to the C++ compilerCPPFLAGS
: Extra flags to give to the C preprocessorLDFLAGS
: Extra flags to give to compilers when they are supposed to invoke the linker
Let's see how we can now build a C program without ever explicitly telling Make how to do the compililation:
blah: blah.o
blah.c:
echo "int main() { return 0; }" > blah.c
clean:
rm -f blah*
lyao-che@u80z03s08 42 % make blah
echo "int main() { return 0; }" > blah.c
gcc -g -c -o blah.o blah.c
gcc blah.o -o blah
lyao-che@u80z03s08 42 % ls -la
total 64
drwxr-xr-x 6 lyao-che 2022_kuala-lumpur 204 6 Jun 10:07 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 337 6 Jun 10:07 Makefile
-rwxr-xr-x 1 lyao-che 2022_kuala-lumpur 16736 6 Jun 10:07 blah
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 25 6 Jun 10:07 blah.c
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 1856 6 Jun 10:07 blah.o
lyao-che@u80z03s08 42 % make clean
rm -f blah*
lyao-che@u80z03s08 42 % ls -la
total 8
drwxr-xr-x 3 lyao-che 2022_kuala-lumpur 102 6 Jun 10:07 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 337 6 Jun 10:07 Makefile
Static pattern rules are another way to write less in a Makefile, but I'd say are more useful and a bit less "magic". Here's their syntax:
targets...: target-pattern: prereq-patterns ...
commands
The essence is that the given target
is matched by the target-pattern
(via a %
wildcard). Whatever was matched is called the stem. The stem is then substituted into the prereq-pattern
, to generate the target's prereqs.
A typical use case is to compile .c
files into .o
files. Here's the manual way:
objects = foo.o bar.o all.o
all: $(objects)
# These files compile via implicit rules
foo.o: foo.c
bar.o: bar.c
all.o: all.c
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $@
clean:
rm -f *.c *.o all
lyao-che@u80z03s08 42 % make
echo "int main() { return 0; }" > all.c
cc -c -o all.o all.c
touch foo.c
cc -c -o foo.o foo.c
touch bar.c
cc -c -o bar.o bar.c
cc all.o foo.o bar.o -o all
lyao-che@u80z03s08 42 % ls -la
total 80
drwxr-xr-x 10 lyao-che 2022_kuala-lumpur 340 6 Jun 10:26 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 217 6 Jun 10:24 Makefile
-rwxr-xr-x 1 lyao-che 2022_kuala-lumpur 16536 6 Jun 10:26 all
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 25 6 Jun 10:26 all.c
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 616 6 Jun 10:26 all.o
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 0 6 Jun 10:26 bar.c
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 10:26 bar.o
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 0 6 Jun 10:26 foo.c
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 10:26 foo.o
lyao-che@u80z03s08 42 % make clean
rm -f *.c *.o all
lyao-che@u80z03s08 42 % ls -la
total 8
drwxr-xr-x 3 lyao-che 2022_kuala-lumpur 102 6 Jun 10:29 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 217 6 Jun 10:24 Makefile
Here's the more efficient way, using a static pattern rule:
objects = foo.o bar.o all.o
all: $(objects)
# These files compile via implicit rules
# Syntax - targets ...: target-pattern: prereq-patterns ...
# In the case of the first target, foo.o, the target-pattern matches foo.o and sets the "stem" to be "foo".
# It then replaces the '%' in prereq-patterns with that stemfoo.o: foo.c
$(objects): %.o: %.c
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $@
clean:
rm -f *.c *.o all
lyao-che@u80z03s08 42 % make
echo "int main() { return 0; }" > all.c
cc -c -o all.o all.c
touch foo.c
cc -c -o foo.o foo.c
touch bar.c
cc -c -o bar.o bar.c
cc all.o foo.o bar.o -o all
lyao-che@u80z03s08 42 % ls -la
total 80
drwxr-xr-x 10 lyao-che 2022_kuala-lumpur 340 6 Jun 10:35 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 440 6 Jun 10:35 Makefile
-rwxr-xr-x 1 lyao-che 2022_kuala-lumpur 16536 6 Jun 10:35 all
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 25 6 Jun 10:35 all.c
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 616 6 Jun 10:35 all.o
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 0 6 Jun 10:35 bar.c
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 10:35 bar.o
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 0 6 Jun 10:35 foo.c
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 10:35 foo.o
lyao-che@u80z03s08 42 % make clean
rm -f *.c *.o all
lyao-che@u80z03s08 42 % ls -la
total 8
drwxr-xr-x 3 lyao-che 2022_kuala-lumpur 102 6 Jun 10:36 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 440 6 Jun 10:35 Makefile
While I introduce functions later on, I'll foreshadow what you can do with them. The filter
function can be used in Static pattern rules to match the correct files. In this example, I made up the .raw
and .result
extensions.
.phony
- These special targets are called phony and you can explicitly tell Make they're not associated with files : Good examples for this are the common targets "clean" and "all".
obj_files = foo.result bar.o lose.o
src_files = foo.raw bar.c lose.c
.PHONY: all
all: $(obj_files)
$(filter %.o, %(obj_files)): %.o: %.c
echo "target: $@ prereq: $<"
$(filter %.result, $(obj_files)): %.result: %.raw
echo "target: $@ prereq: $<"
%.c %.raw:
touch $@
clean:
rm -f $(src_files)
lyao-che@u81z01s01 42 % make
touch foo.raw
echo "target: foo.result prereq: foo.raw"
target: foo.result prereq: foo.raw
touch bar.c
cc -c -o bar.o bar.c
touch lose.c
cc -c -o lose.o lose.c
rm bar.c lose.c
lyao-che@u81z01s01 42 % ls -la
total 24
drwxr-xr-x 6 lyao-che 2022_kuala-lumpur 204 6 Jun 15:52 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 299 6 Jun 15:51 Makefile
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 15:52 bar.o
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 0 6 Jun 15:52 foo.raw
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 15:52 lose.o
lyao-che@u81z01s01 42 % make clean
rm -f foo.raw bar.c lose.c
lyao-che@u81z01s01 42 % ls -la
total 24
drwxr-xr-x 5 lyao-che 2022_kuala-lumpur 170 6 Jun 15:56 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 299 6 Jun 15:51 Makefile
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 15:52 bar.o
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 15:52 lose.o
lyao-che@u81z01s01 42 % make all
touch foo.raw
echo "target: foo.result prereq: foo.raw"
target: foo.result prereq: foo.raw
lyao-che@u81z01s01 42 % ls -la
total 24
drwxr-xr-x 6 lyao-che 2022_kuala-lumpur 204 6 Jun 15:57 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 299 6 Jun 15:51 Makefile
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 15:52 bar.o
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 0 6 Jun 15:57 foo.raw
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 6 Jun 15:52 lose.o
Pattern rules are often used but quite confusing. You can look at them as two ways:
- A way to define your own implicit rules
- A simpler form of static pattern rules
Let's start with an example first:
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
lyao-che@u81z01s01 42 % vim test.c
lyao-che@u81z01s01 42 % make test.o
cc -c test.c -o test.o
lyao-che@u81z01s01 42 % ls -la
total 24
drwxr-xr-x 5 lyao-che 2022_kuala-lumpur 170 8 Jun 07:38 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 118 8 Jun 07:35 Makefile
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 31 8 Jun 07:38 test.c
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 616 8 Jun 07:38 test.o
Pattern rules contain a %
in the target. This %
matches any nonempty string, and the other characters match themselves. %
in a prerequisite of a pattern rule stands for the same stem that was matched by the %
in the target.
Here's another example:
# Define a pattern rule that compiles every .c file into a .o file
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
# Define a pattern rule that has no pattern in the prerequisites.
# This just creates empty .c files when needed.
%.c:
touch $@
lyao-che@u81z01s01 42 % make test.o
touch test.c
cc -c test.c -o test.o
rm test.c
lyao-che@u81z01s01 42 % ls -la
total 16
drwxr-xr-x 4 lyao-che 2022_kuala-lumpur 136 8 Jun 07:52 .
drwxr-xr-x 22 lyao-che 2022_kuala-lumpur 748 6 Jun 10:03 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 248 8 Jun 07:51 Makefile
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 208 8 Jun 07:52 test.o
Double-Colon Rules are rarely used, but allow multiple rules to be defined for the same target. If these were single colons, a warning would be printed and only the second set of commands would run.
all: blah
blah::
echo "hello"
blah::
echo "hello again"
make
echo "hello"
hello
echo "hello again"
hello again
lyao-che@u81z01s01 42 % ls -la
total 8
drwxr-xr-x 3 lyao-che 2022_kuala-lumpur 102 8 Jun 10:20 .
drwxr-xr-x 23 lyao-che 2022_kuala-lumpur 782 8 Jun 09:41 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 60 8 Jun 10:20 Makefile
Add an @
before a command to stop it from being printed
You can also run make with -s
to add an @
before each line
all:
@echo "this make line will not be printed"
echo "But this will"
lyao-che@u81z01s01 42 % vim Makefile
lyao-che@u81z01s01 42 % make
this make line will not be printed
echo "But this will"
But this will
Each command is run in a new shell (or at least the effect is as such)
all:
cd ..
# The cd above does not affect this line, because each command is effectively run in a new shell
echo `pwd`
# This cd command affects the next because they are on the same line
cd ..;echo `pwd`
# Same as above
cd ..; \
echo `pwd`
Makefile
lyao-che@u81z01s01 42 % vim Makefile
lyao-che@u81z01s01 42 % make
cd ..
# The cd above does not affect this line, because each command is effectively run in a new shell
echo `pwd`
/Users/lyao-che/Desktop/42
# This cd command affects the next because they are on the same line
cd ..;echo `pwd`
/Users/lyao-che/Desktop
# Same as above
cd ..; \
echo `pwd`
/Users/lyao-che/Desktop
The default shell is /bin/sh. You can change this by changing the variable SHELL:
SHELL=/bin/bash
cool:
echo "Hello from bash"
Add -k
when running make to continue running even in the face of errors. Helpful if you want to see all the errors of Make at once.
Add a -
before a command to suppress the error
Add -i
to make to have this happen for every command.
one:
# This error will be printed but ignored, and make will continue to run
-false
touch one
lyao-che@u81z01s01 42 % make
# This error will be printed but ignored, and make will continue to run
false
make: [one] Error 1 (ignored)
touch one
lyao-che@u81z01s01 42 % make
# This error will be printed but ignored, and make will continue to run
k
make: k: No such file or directory
make: [one] Error 1 (ignored)
touch one
one:
# This error will be printed but ignored, and make will continue to run
-k
touch one
Note only: If you ctrl+c
make, it will delete the newer targets it just made.\
To recursively call a makefile, use the special $(MAKE) instead of make because it will pass the make flags for you and won't itself be affected by them.
all:
mkdir -p subdir
printf $(new_contents) | sed -e 's/^ //' > subdir/makefile
cd subdir && $(MAKE)
clean:
rm -rf subdir
Lims-iMac:42 YCLim$ make
mkdir -p subdir
printf "hello:\n\ttouch inside_file" | sed -e 's/^ //' > subdir/makefile
cd subdir && /Library/Developer/CommandLineTools/usr/bin/make
touch inside_file
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 "hello:\n\ttouch
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 &&
drwxr-xr-x 3 YCLim staff 102 Jun 9 12:47 's
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 -e
drwxr-xr-x 16 YCLim staff 544 Jun 9 12:47 .
drwx------@ 6 YCLim staff 204 Jun 5 15:44 ..
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 >
-rw-r--r-- 1 YCLim staff 172 Jun 9 12:47 Makefile
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 cd
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 inside_file
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 inside_file"
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 printf
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 sed
drwxr-xr-x 4 YCLim staff 136 Jun 9 12:47 subdir
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 touch
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 |
Lims-iMac:42 YCLim$ cd subdir/
Lims-iMac:subdir YCLim$ ls -la
total 8
drwxr-xr-x 4 YCLim staff 136 Jun 9 12:47 .
drwxr-xr-x 16 YCLim staff 544 Jun 9 12:47 ..
-rw-r--r-- 1 YCLim staff 0 Jun 9 12:48 inside_file
-rw-r--r-- 1 YCLim staff 26 Jun 9 12:48 makefile
Lims-iMac:42 YCLim$ make clean
rm -rf subdir
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 "hello:\n\ttouch
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 &&
drwxr-xr-x 3 YCLim staff 102 Jun 9 12:47 's
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 -e
drwxr-xr-x 15 YCLim staff 510 Jun 9 12:55 .
drwx------@ 6 YCLim staff 204 Jun 9 12:54 ..
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 >
-rw-r--r-- 1 YCLim staff 172 Jun 9 12:47 Makefile
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 cd
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 inside_file
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 inside_file"
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 printf
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 sed
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 touch
drwxr-xr-x 2 YCLim staff 68 Jun 9 12:47 |
subdir/makefile
hello:
touch inside_file
The export directive takes a variable and makes it accessible to sub-make commands. In this example, cooly
is exported such that the makefile in subdir can use it.
Note: export has the same syntax as sh, but they aren't related (although similar in function)
Lims-iMac:42 YCLim$ cat makefile
new_contents = "hello:\n\\techo \$$(cooly)"
all:
mkdir -p subdir
echo $(new_contents) | sed -e 's/ ^ //' > subdir/makefile
@echo "---MAKEFILE CONTENTS---"
@cd subdir && cat makefile
@echo "---END MAKEFILE CONTENTS---"
cd subdir && $(MAKE)
# Note that variables and exports. They are set/affected globally.
cooly = "The subdirectory can see me!"
export cooly
# This would nullify the line above: unexport cooly
clean:
rm -rf subdir
Lims-iMac:42 YCLim$ make
mkdir -p subdir
echo "hello:\n\\techo \$(cooly)" | sed -e 's/ ^ //' > subdir/makefile
---MAKEFILE CONTENTS---
hello:
echo $(cooly)
---END MAKEFILE CONTENTS---
cd subdir && /Library/Developer/CommandLineTools/usr/bin/make
echo "The subdirectory can see me!"
The subdirectory can see me!
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 4 YCLim staff 136 Jun 9 16:10 .
drwx------@ 6 YCLim staff 204 Jun 9 16:06 ..
-rw-r--r-- 1 YCLim staff 441 Jun 9 16:10 Makefile
drwxr-xr-x 3 YCLim staff 102 Jun 9 16:10 subdir
Lims-iMac:42 YCLim$ cd subdir/
Lims-iMac:subdir YCLim$ ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 9 16:10 .
drwxr-xr-x 4 YCLim staff 136 Jun 9 16:10 ..
-rw-r--r-- 1 YCLim staff 22 Jun 9 16:10 makefile
Lims-iMac:42 YCLim$ make clean
rm -rf subdir
Lims-iMac:42 YCLim$ ls -la
total 8
drwxr-xr-x 3 YCLim staff 102 Jun 9 16:14 .
drwx------@ 6 YCLim staff 204 Jun 9 16:06 ..
-rw-r--r-- 1 YCLim staff 441 Jun 9 16:10 Makefile
subdir/makefile
hello:
echo $(cooly)
You need to export variables to have them run in the shell as well.
one=this will only work locally
export two=we can run subcommands with this
all:
@echo $(one)
@echo $$one
@echo $(two)
@echo $$two
lyao-che@u81z01s01 42 % make
this will only work locally
we can run subcommands with this
we can run subcommands with this
lyao-che@u81z01s01 42 % ls -la
total 8
drwxr-xr-x 3 lyao-che 2022_kuala-lumpur 102 10 Jun 07:47 .
drwxr-xr-x 24 lyao-che 2022_kuala-lumpur 816 8 Jun 15:49 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 136 10 Jun 07:47 Makefile
.EXPORT_ALL_VARIABLES exports all variables for you.
.EXPORT_ALL_VARIABLES:
new_contents = "hello:\n\techo \$$(cooly)"
cooly = "The subdirectory can see me!"
# This would nullify the line above: unexport cooly
all:
mkdir -p subdir
echo $(new_contents) | sed -e 's/^ //' > subdir/makefile
@echo "---MAKEFILE CONTENTS---"
@cd subdir && cat makefile
@echo "---END MAKE FILE CONTENTS---"
cd subdir && $(MAKE)
clean:
rm -rf subdir
lyao-che@u81z01s01 42 % make
mkdir -p subdir
echo "hello:\n\techo \$(cooly)" | sed -e 's/^ //' > subdir/makefile
---MAKEFILE CONTENTS---
hello:
echo $(cooly)
---END MAKE FILE CONTENTS---
cd subdir && /Applications/Xcode.app/Contents/Developer/usr/bin/make
echo "The subdirectory can see me!"
The subdirectory can see me!
lyao-che@u81z01s01 42 % ls -la
total 8
drwxr-xr-x 4 lyao-che 2022_kuala-lumpur 136 10 Jun 07:51 .
drwxr-xr-x 24 lyao-che 2022_kuala-lumpur 816 8 Jun 15:49 ..
-rw-r--r-- 1 lyao-che 2022_kuala-lumpur 383 10 Jun 07:51 Makefile
drwxr-xr-x 3 lyao-che 2022_kuala-lumpur 102 10 Jun 07:51 subdir
lyao-che@u81z01s01 42 % cd subdir
lyao-che@u81z01s01 subdir % ls
makefile
subdir/makefile
hello:
echo $(cooly)
There's a nice list of options that can be run from make. Check out --dry-run
, --touch
, --old-file
.
You can have multiple targets to make, i.e. make clean run test
runs the clean
goal, then run
, and then test
.
There are two flavors of variables:
- recursive (use
=
) - only looks for the variables when the command is used, not when it's defined. - simply expanded (use
:=
) - like normal imperative programming -- only those defined so far get expanded
# Recursive variable. This will print "later" below
one = one ${later_variable}
# Simply expanded variable. This will not print "later" below
two := two ${later_variable}
later_variable = later
all:
echo $(one)
echo $(two)
lyao-che@u81z01s01 42 % make
echo one later
one later
echo two
two
Simply expanded (using :=
) allows you to append to a variable. Recursive definitions will give an infinite loop error.
one = hello
# one gets defined as a simply expanded variable (:=) and thus can handle appending
one := ${one} there
all:
echo $(one)
lyao-che@u81z01s01 42 % make
echo hello there
hello there
?=
only sets variables if they have not yet been set
one = hello
one ?= will not be set
two ?= will be set
all:
echo $(one)
echo $(two)
lyao-che@u81z01s01 42 % make
echo hello
hello
echo will be set
will be set
Spaces at the end of a line are not stripped, but those at the start are. To make a variable with a single space, use $(nullstring)
with_spaces = hello # with_spaces has many spaces after "hello"
after = $(with_spaces)there
nullstring =
space = $(nullstring) # Make a variable with a single space.
all:
echo "$(after)"
echo start"$(space)"end
lyao-che@u81z01s01 42 % make
echo "hello there"
hello there
echo start" "end
start end
An undefined
variable is actually an empty string!
all:
# Undefined variables are just empty strings!
echo $(nowhere)
lyao-che@u81z01s01 42 % make
# Undefined variables are just empty strings!
echo
Use +=
to append
foo := start
foo += more
all:
echo $(foo)
lyao-che@u81z01s01 42 % make
echo start more
start more