Linux/make

From Omnia
Revision as of 03:22, 30 June 2023 by Kenneth (talk | contribs) (→‎change directory)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

make

The principal domain, of 'make', is C programming. Many sites also use 'make' for software installation, document formatting, and cleaning out temporary files.

Make file uses backward-chaining to process a Makefile (description file).

GNU Make Documentation

GNU Make Documentation

Tutorials

Books

Managing Projects with GNU Make, 3rd Edition - O'Reilly Media - http://oreilly.com/catalog/make3/book/index.csp

PDF - http://it-ebooks.info/book/383/

usage

make [target(s)]
make -f mymakefile [target(s)]

validation

Check the tabs in description file:

cat -v -t -e makefile

file name

To specify make description file name: make -f filename.

When GNU Make is run, with no arguments, it will look for one of the following configuration files, in the current working directory: (in priority order, is also directory sort order)

  1. GNUmakefile
  2. makefile
  3. Makefile

"If no -f option is present, make will look for the makefiles GNUmakefile, makefile, and Makefile, in that order." (make man page)

-

"This file is normally named makefile or Makefile. As a convention, GNU programs named their makefile, Makefile, because it is easy to see (if you do "ls" then this file is usually always on the top of the list). If you give it another name, just make sure you include option -f to make command in order to let it know that you use it." [1]

Layout

Makefile layout:

  • dependency line / rule line (targets:dependencies)
  • command lines (tab command)
  • comments (# comment)

Lines can be wrapped using the '\' character at the end of a line.

A Makefile is composed of rules. The rules are composed of targets, dependencies (prerequisites) and system commands. The first target in the Makefile is the default target.

A Makefile rule is composed of:

target(s) : dependencies
[tab] system commands

Examples:

# Linking object files
sample: main.o example.o
	cc -o sample main.o example.o
	echo sample: make complete
# Compiling source files
main.o: main.c main.h
	cc -c main.c
example.o: example.c defs.h
	cc -c example.c
new_Spec new_impt : menus hash store
 date >> $@
 ls $? >> $@

targets

target : dependency
target target2 : dependency dependency2

Able to reuse target with multiple colons:

target :: dependency1
target :: dependency2

Logic

A target is considered "up to date" if an identically named file exists and is newer than its dependencies.

Make works backwards, starting with the target of the first rule in the file. In our example, that's sample. Make checks the dependencies for sample -- main.o and example.o -- to see if they have rules. If they do, it recursively checks their rules.

Make walks down the recursion chain until it finds a target that has no dependencies, or whose dependencies have no rules. Once it hits one of those, it walks back up its recursion chain and runs commands as necessary. It creates a recursion chain for every prerequisite it encounters that has a rule.

Make can run the dependencies in any order. The important part of this sequence is that it runs recursively backwards from the first target (or the target named in the command parameters), and tests only the rules that it encounters in the dependencies chain.

Make aborts compilation if it receives an error. This is usually useful behavior -- it lets you correct compiler-detected problems during a compile-and-test cycle. The option -i tells Make to ignore errors.

make will check the last modification date of target against the last modification date of all the dependencies files that follow it. If none of these things have changed, then it won't recompile the target.

Default Target

The default target is the first target found.

Phony Target

A phony target is a fake filename. It is just a name for commands that will be executed when you give an explicit request. There are two reasons for using phony target : to avoid conflicts with a file with the same name, and to enhance the makefile performance.

If you write a rule whose command will not create a target file, those commands will be executed every time the target is remade. For example:

clean:
	rm *.o temp

Because the command rm will not create a file named clean, that file will never exist. Command rm will always be executed every time you called make clean, because make assume that the clean file is always new.

The above target will stop working if a file named clean exists in the current directory. Because it does not require dependencies, file clean will be considered up-to-date, and the command 'rm *.o temp' will not be executed. To resolve this problem, you can explicitly declare a target as phony, using special target command .PHONY. For example :

.PHONY : clean

In the makefile above, if we give instruction make clean from the command-line, the command 'rm *.o temp' will always be run, whether or not a file named clean exists in the current directory.

Example:

.PHONY : all test1 test2

all: test1 test2

test1:
        cd test1 ; tar -cf ../test1.tar *

test2:
        cd test2 ; tar -cf ../test2.tar *

Target name. To reference from within a command use '$@'

test1:
        cd $@ ; tar -cf ../$@.tar *

Source: Creating Makefiles: A Mini Tutorial LG #83

Wildcard Target

# make all
all:
	echo %@  # "all"
# make test
%:
	echo %@  # "test"
# make test.txt
%.txt:
	echo %@  # "test"
# make all
# output: "first" "second"
.PHONY: all first second
TARGETS=first second

all: $(TARGETS)

$(TARGETS):
	@echo $@

Ignore Error

Start a line with '-' to continue even on error:

-command_that_fails

Comments

Comments are start with a '#':

# This is a comment

End of line comment:

   some makefile statement # a comment 

Comments and Continued Makefile Lines:

line_one \
line_two # more_line_two \
line_three 

is the same as:

   line_one line_two line_three 

Source: http://www.opussoftware.com/tutorial/TutMakefile.htm

Hide Command

The '@' character will hide the command name, but show command output.

usage:
	@echo "This is the usage"

macros

macro definitions:

name = string
name=string
name = some strings
name =    # set to null string, not "undefied"
name = "some strings"  # " will be included in output

macro usage:

$(name)
${name}
$A  # optional only with single character macros (or special character macros)

Define from command line:

make DIR=/path
export DIR=path ; make  # shell environment form
DIR=path make  # shell environment form

Macro assignment priority order:

  1. Internal (default) definitions of 'make'
  2. Current shell environment variables (including those preceding 'make' command)
  3. Description file macro definitions
  4. Macros specified on 'make' command line

Can swap priority 2 and 3 with make -e

Tips:

  • Macro names, by convention, are in uppercase.
  • If macro referred to without defining, will be null string. Never will get an undefined error.
  • Order of macros is unimportant, will be evaluated when used
  • Macros cannot be redefined, once defined (unless doing a recursive make)
  • Can display all predefined macros with make -p (and there are a lot)
  • Shell/environment variables are available as macros

-

String substitution: (limitation: end of macro only - like extensions)

ls ${SRCS:.c=.o}  # src.c to src.o

-

Internal macros:

$@ - current target (command line only)
$? - prerequisites that are newer than target (command line only)
$$@ - current target (dependency line only)

Example:

new_Spec new_impt : menus hash store
	date >> $@
	ls $? >> $@
docmk : $$@.c  # evaluates to: docmk : docmk.c
C_OBJS = main.o iodat.o dorun.o
${C_OBJS} : $${@:.o=.c}
	${CC} -c $?

Variables or Macros

Certain versions of Make call variables "macros".

Make variables are different from shell variables.

Make's variables are defined and set in the header portion before targets are defined.

Set a Make variable:

MYVAR = one two three  # recursively expanded variable
MYVAR := one two three  # simply expanded variables

Use a Make variable:

# Using variable with another variable
ANOTHER_MYVAR=$(MYVAR) four
# Using variable from target
all:
	echo $(MYVAR)
	echo ${MYVAR}

Setting make variables from environment variables:

DESTDIR ?= /usr/local

An environment variable can also be passed to the Makefile by not defining the variable in the Makefile. If it is not set in the environment, the variable will default to empty.

Passing environment variable to pass to Makefile:

$ DESTDIR="/bin" make
$ export DESTDIR="/bin" ; make
DESTDIR="/bin" make

Append variable (will also append to the environment variables)

CFLAGS += -g -Wall

Substitution:

foo := a.o b.o c.o
bar := $(foo:.o=.c)
# sets 'bar' to 'a.c b.c c.c'

Make variable example:

# I am a variable
CC=g++
CFLAGS=-c -Wall

main.o: main.cpp
	$(CC) $(CFLAGS) main.cpp

Redefining Macros from the command line:

make CFLAGS=–ms
make "CFLAGS=-ms -z -p"

Macro Shell Assignment

$(shell) is a special function in gmake that runs an external command and captures the output for use in the makefile. For example, you could get the current working directory like this:

CWD:=$(shell pwd)
all:
	@echo This makefile lives in $(CWD).

Note, make sure to use ":=" or it will be reevaluated in each new section, unless that is what you want

DATE=$(warning Invoking shell)$(shell date)
all: first
	@echo ${DATE}

first:
	@echo ${DATE}
        sleep 3

References:

Macro Functions

$(dir names...)         # Extracts the directory-part of each file name in names.
$(basename names...)    # Extracts all but the suffix of each file name in names.
$(addprefix prefix,names...)    # The argument names is regarded as a series of names, separated by whitespace; prefix is used as a unit.

File Name Functions - GNU `make' - https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html

Warn on Undefined

Warn on undefined variables:

--warn-undefined-variables

Warning

Display warning macro:

$(warning This is a warning)

Shell Variables

Shell variables in Makefiles

There may come a time you need to use shell scripting complicated enough to require shell vars in a Makefile but make has issues since $ is the prefix for Make vars too, to escape the $, just use $$, so this:

for e in * ; do echo $e ; done

becomes:

for e in * ; do echo $$e ; done


Another method is to embed within a bash command: [2]

/bin/sh -c 'i="test" ; echo $$i'

Multiple Combined Commands

Each line runs in it's own shell. If you would like to chain several commands together (usually to get to shell variables):

	line_one \
	line_two # more_line_two \
	line_three 
%.pem:
        umask 77 ; \
        PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \
        PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \
        /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) ; \
        cat $$PEM1 >  $@ ; \
        echo ""    >> $@ ; \
        cat $$PEM2 >> $@ ; \
        $(RM) $$PEM1 $$PEM2

change directory

How do you change directories in a makefile, run a command and then change back? First off, all the commands that you want to run for that situation must be on the same line, because make executes each line with a new shell

foo:
    cd /bin; stat ls; cd -

Source: http://crawlicious.com/wp/2009/06/11/make-change-dir/

Include Other Makefiles

include filenames...

ref: https://www.gnu.org/software/make/manual/html_node/Include.html

Info and Error Output

Info message:

$(info [message])

Error message:

$(error [message])

Example:

HELLO=hello world
all:
        $(info hi $(HELLO))
        $(error this is a test)

Conditionals

USERID=$(shell id -u)

all:
	$(info User Id is $(USERID))
ifeq ($(USERID),0)
	$(info Running as root... OK)
else
	$(error You must be root to execute this command)
endif

Source: [3]


If file1 does not exist then $(wildcard file1) will evaluate to an empty string:

ifeq ($(wildcard file1),) 
    CLEAN_SRC =
else 
    CLEAN_SRC = *.h file3
endif

Source: http://stackoverflow.com/questions/1077676/how-to-conditional-set-up-a-makefile-variable-by-testing-if-a-file-exists

References:

Suffixes

Define new default behavior for targets

.SUFFIXES : .txt .rst

.txt.rst :
    cat $< > $@
    # cat test.txt > test.rst
    # or cat $@ > $*.rst

manual : test.rst
  • $< evaluates to prerequisite that triggered the rule (test.txt)
  • $* evaluates to target minus suffix
  • $@ evaluates to target

Example Make Files

See Linux/make/Examples

Postfix main.cf Makefile:

# Kenneth

.PHONY : aliases
.PHONY : clean

MAIN-FILES = main.cf.original main.cf.kenneth

all: main.cf aliases

clean:
        # CLEANING FILES
        #
        rm -f main.cf
        #

main.cf: $(MAIN-FILES)
        # REBUILDING MAIN.CF...
        #
        cat main.cf.original main.cf.kenneth > main.cf
        #

aliases:
        # REBUILDING ALIASES...
        #
        newaliases
        #

$(MAIN-FILES):

Examples

Reference

keywords