Skip to content

Instantly share code, notes, and snippets.

@yclim95
Last active June 10, 2022 03:18
Show Gist options
  • Save yclim95/50bf380b6af068a9ceaabe469a37eccf to your computer and use it in GitHub Desktop.
Save yclim95/50bf380b6af068a9ceaabe469a37eccf to your computer and use it in GitHub Desktop.
Makefile

Makefile

1. Beginner

1.1. Makefile syntax

targets: prerequisites
	command
	command
	command

1.2. Build a program using Makefile

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.

1.3. Clean (Remove) Build Program

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

1.4. Always run both targets

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.

2. Variables

2.1. Variables (begineer)

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

2.2. Reference variables & Bad practice

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

3. Targets

3.1. ALL Targets

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

3.2. Multiple targets

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

4. Automatic Variables & Wildcards

4.1. * Wildcard

  1. Both * and % are called wildcards in Make, but they mean entirely different things.
  2. * searches your filesystem for matching filenames.
  3. 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

4.1.1 * Wildcard Right & Wrong

* 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'.

4.2. % Wildcard

% is really useful, but is somewhat confusing because of the variety of situations it can be used in.

  1. When used in "matching" mode, it matches one or more characters in a string. This match is called the stem.
  2. When used in "replacing" mode, it takes the stem that was matched and replaces that in a string.
  3. % is most often used in rule definitions and in some specific functions.

See these sections on examples of it being used:

  1. Static Pattern Rules
  2. Pattern Rules
  3. String Substitution
  4. The vpath Directive

5. Automatic Variables

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

6. Fancy Rules

6.1. Implicit Rules

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:

  1. Compiling a C program: n.o is made automatically from n.c with a command of the form $(CC) -c $(CPPFLAGS) $(CFLAGS)
  2. Compiling a C++ program: n.o is made automatically from n.cc or n.cpp with a command of the form $(CXX) -c $(CPPFLAGS) $(CXXFLAGS)
  3. Linking a single object file: n is made automatically from n.o by running the command $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)

The important variables used by implicit rules are:

  1. CC: Program for compiling C programs; default cc
  2. CXX: Program for compiling C++ programs; default g++
  3. CFLAGS: Extra flags to give to the C compiler
  4. CXXFLAGS: Extra flags to give to the C++ compiler
  5. CPPFLAGS: Extra flags to give to the C preprocessor
  6. LDFLAGS: 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

6.2. Static Pattern Rules

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

6.3. Static Pattern Rules and Filter

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

6.3.1. Pattern Rules

Pattern rules are often used but quite confusing. You can look at them as two ways:

  1. A way to define your own implicit rules
  2. 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

6.3.2. Double-Colon Rules

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

7. Commands and execution

7.1. Command Echoing/Silencing

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

7.2. Command Execution

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

7.3. Default Shell

The default shell is /bin/sh. You can change this by changing the variable SHELL:

SHELL=/bin/bash

cool:
	echo "Hello from bash"

7.4. Error handling with -k, -i, and -

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

7.5. Interrupting or killing make

Note only: If you ctrl+c make, it will delete the newer targets it just made.\

7.6. Recursive use of make

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

7.7. Use export for recursive make

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)

7.8. Arguments to make

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.

8. Variables Pt. 2

8.1. Flavors and modification

There are two flavors of variables:

  1. recursive (use =) - only looks for the variables when the command is used, not when it's defined.
  2. 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment