Make Concepts

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

Internal

make Command Line

make will interpret the command line tokens as: targets, definitions and execution flags (options). The arguments not recognized as definitions or execution flags are automatically interpreted as targets.

Anything that starts with "--" is an option, and it has to be known to make to be accepted.

TODO: https://askubuntu.com/a/1448109

Rule

https://www.gnu.org/software/make/manual/make.html#Rules

A makefile consists of rules with the following syntax:

target ...: prerequisites
    recipe

A target can be either the name of a file that is generated by the recipe, or the name of an action to carry out. These are phony named phony targets, because they are not files.

A prerequisite is a file that is used as input to create the target. A target often depends on several files.

A recipe is an action that "make" carries out. A recipe may have more than one command, either on the same line or each on its own line. You need to put a tab character at the beginning of every recipe line. Is this still true? Usually the recipe (re-)creates the target file if any of the prerequisites change. However, the rule that specifies a recipe for the target need not have prerequisites.

make Executes only the First Rule By Default

https://www.gnu.org/software/make/manual/html_node/How-Make-Works.html

If not told otherwise, make will build the first target, along its prerequisites, then stop. If more than one files need to be generate, group them under common target, and put it in the first position in the file, or use .DEFAULT_GOAL.

.PHONY: generate_oapi_artifacts

generate_oapi_artifacts: spec.gen.go types.gen.go

spec.gen.go: ./petstore.yaml
	oapi-codegen -generate spec -package petstore $< > $@

types.gen.go: ./petstore.yaml
	oapi-codegen -generate types -package petstore $< > $@

Recipe

https://www.gnu.org/software/make/manual/make.html#Recipes

Recipe Echo

https://www.gnu.org/software/make/manual/make.html#Echoing

By default, make prints each line of the recipe before it is executed.

When a line starts with "@", the echoing is supressed.

experiment:
	@echo ${COLOR}


Recipes Preprocessing

make pre-processes the recipe line before sending it to a shell (each line is sent to a different shell).

Preprocessing:

  • Because dollar signs are used to start make variable references, if you really want a dollar sign in a target or prerequisite you must write two of them, '$$'. If you have enabled secondary expansion and you want a literal dollar sign in the prerequisites list, you must actually write four dollar signs ('$$$$').

Recipes and Executing Shells

make starts new sub shell for each line of the recipe.

Setting Environment Variables for Recipe Commands

Write all the environment variables you want to set in the environment of the recipe command in a file, possibly with another target:

Source the file on the same line as the command you want to provide environment variables to (this is because make starts a sub-shell for each recipe line.

pre-experiment:
	echo "COLOR=FUCHSIA" > $(CURDIR)/build/.env
experiment: pre-experiment
	. $(CURDIR)/build/.env; report-color

To specify an environment variable verbatim in a recipe, you will need to use $$ to prevent make from doing its own variable substitution. Also, because different lines are executed by different shells, the definition of the variable and the use of the variable must be on the same line.

some-target:
  COLOR="red"; echo "$${COLOR}"

.PHONY

https://www.gnu.org/software/make/manual/make.html#Phony-Targets

By default, make targets are assumed to be files on disk. They will be built from other files as result of executing make with that name as argument. However, sometimes you want make to run commands that do not represent physical files in the filesystem, and if there's a file with the same name in the filesystem, make will be confused and will pick up the file. To avoid this, you can disambiguate with .PHONY:

.PHONY: clean
clean: 
  rm -rf *.o

.DEFAULT_GOAL

https://www.gnu.org/software/make/manual/html_node/Special-Variables.html#index-_002eDEFAULT_005fGOAL-_0028define-default-goal_0029
.DEFAULT_GOAL := some_target

Variables

CURDIR

CURDIR refers to the directory make is run from:

something:
	"$(CURDIR)/scripts/something.sh"

Automatic Variables

https://www.gnu.org/software/make/manual/make.html#Automatic-Variables

$@

The file name of the target of the rule. In a pattern rule that has multiple targets $@ is the name of whichever target caused the rule’s recipe to be run.

$<

The name of the first prerequisite. For this rule:

types.gen.go: petstore.yaml
	oapi-codegen -generate types -package petstore $< > $@

$< contains "petstore.yaml"

Custom Variables

https://www.gnu.org/software/make/manual/make.html#Using-Variables

Variables allow a text string to be defined once and substituted in multiple places later. A variable is a name defined in Makefile to represent a string of text, called the variable's value. These value are substituted in targets, prerequisites and other parts of the make file. A variable name may be any sequence of characters not containing ":", "#", "=", or whitespace. Variable names beginning with "." and an uppercase letter may be given special meaning in future versions of make. Variable names are case sensitive. It is traditional to use upper case letters in variable names, but we recommend using lower case letters for variable names that serve internal purposes in the makefile, and reserving upper case for parameters that control implicit rules or for parameters that the user should override with command options.

This is how a variable is declared:

my_var = something

This is how a variable is used:

prog.o: $(my_var)

Variables can be used in the value of other variables:

my_var = something
my_other_var = ./$(my_var)/other

Initializing a Custom Variable with the Result of a Command Execution

Use the shell function:

some_var = $(shell $(CURDIR)/scripts/some-script.sh)

Functions

shell Function

https://www.gnu.org/software/make/manual/make.html#Shell-Function

The shell function does command expansion: it takes as an argument a shell command and executes the command, expanding it to the output of the the command:

test: build
	env	MY_PRIVATE_KEY=$(shell ./scripts/get-my-private-key) go test ./...

The shell function removes the trailing new lines and replaces the intermediate new lines with spaces.

!= Shell Assignment Operator

https://www.gnu.org/software/make/manual/make.html#Setting

The != shell assignment operator is similar to the shell function. It removes the trailing new lines and replaces the intermediate new lines with spaces.