Skip to content

Instantly share code, notes, and snippets.

@Contextualist
Created February 9, 2019 06:37
Show Gist options
  • Save Contextualist/e867c036047a7ac905fe7dacc65c1b24 to your computer and use it in GitHub Desktop.
Save Contextualist/e867c036047a7ac905fe7dacc65c1b24 to your computer and use it in GitHub Desktop.

I Want to...

Just Do It

Save the following to a file named Makefile.

# Tell Make that these are symbolic targets, not file names.
.PHONY: install1 install2 install_all parse hello

# Name of the TARGET, followed by TAB-indented commands
install1:
	touch nothing
	mv nothing something

# This target has a PREREQUISITE, which is executed
# before the execution of the commands.
parse: install1
	echo "content" >> something

install2:
	@# Prepend `@` to a command or a comment so that 
	@# it is not displayed.
	@echo "Yay"

# This target has no commands, so it will just execute
# all of its prerequisites in order.
install_all: install1 install2

# You can define variables for repetitive/dynamic names ...
py := env python -c
# ... and call them like this:
hello:
	$(py) "print('hello')"
	$(py) "import sys; print(sys.version)"

To run the set of commands just make TARGET, e.g. make install_all.

New File as Target

# The name of the TARGET is the file you want to create,
# followed by the commands (TAB indented) that create it.
# Note that it is the commands that create the file, not
# MAKE. What MAKE does is that it ensures the commands
# will only run if no file (e.g. with name `thing.txt`)
# exists, therefore preventing redundant execution.
thing.txt:
	echo "important stuff" > thing.txt

# PATTERN matching: in this way, all filenames end with
# .txt will trigger this rule. (e.g. `make 1.txt`)
%.txt:
	# `$@` is a special variable that stands for
	# the target name.
	echo "Going to make $@"

File Prerequisite

# Unlike PHONY PREREQUISITE, which simply run before the
# target's commands, FILE PREREQUISITE of FILE TARGET
# create a dependency:
# 	The target's commands will only run if prerequisite
#	file is newer than target file. (Some changes happened;
#	regenerate the target.)
# Still, if the prerequisite file does not exist, Make will try
# to run the prerequisite rule (In this case, `src.c`).
gen.c: src.c
	cat src.c | rev > gen.c

# Pattern matching is more useful for prerequisites.
# Note that the two `%` match the same thing. If you call 
# `make a-rev.txt`, the prerequisite is `a-src.txt`.
%-rev.txt: %-src.txt
	# `$^` is a special variable that stands for
	# the prerequisite name.
	cat $^ | rev > $@

# Bonus: FILE PREREQUISITE of PHONY TARGET
# no magic, it just acts like an alias (why? consider phony
# as the oldest file).
.PHONY: shortname
shortname: a_file_with_superlong_name.txt
a_file_with_superlong_name.txt:
	# do something...

Match Them All

# So now we have a pattern matching rule:
%-out.txt: %.py
	python $^ > $@

# However it can only match and run one `.py` file at a time.
# What if we want to run all the `.py` files at once?
# Make's functions are your friends!

# Function `wildcard` gives you the name of all files matching
# the pattern.
src_files := $(wildcard *.py)
# Bonus: function `shell` runs shell command and returns the 
# output, in this case, all `.py` file in all the subdirectories
# of `src/`.
#src_files := $(shell find src/ -name '*.py')

# Function `patsubst` replace strings based on pattern (e.g.
# If `src_files` is `a.py b.py`, `output` then becomes
# `a-out.txt b-out.txt`).
output := $(patsubst %.py, %-out.txt, $(src_files))

# Enjoy!
.PHONY: all
all: $(output)

Epilogue

The essence of make is to do merely what is needed. Often people use make to "just get things done", but it is the existence and prerequisite checks that makes it powerful and beautiful.

The tutorial focuses on the most frequently used subset of make. To learn more see: learn Make in Y minutes GNU Make Manual

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment