makefiles: Complex conditional execution

While doing the conditional execution in makefiles, using only if and ifneq allows us to check for one condition but to be able to check multiple conditions one after another, we can use either if or ifneq after the else to check for further conditions.
Syntax:

ifneq(,) statements else statements endif

If the ifneq condition fails then the flow will go to the else and check the condition specified after the else. We can use as many else statements as we require.
Let us say we have three “c” programs, hello_1.c, hello_2.c and hello_3.c. We need to compile them depending on which user is running the make command. hello_1.c should be compiled if we are running make as root, hello_2.c if we are running make as “user1” and for any other user hello_3.c. This can be achieved by the following makefile.

CC = cc -o pf=hello user = $(shell whoami) ifeq ($(user),root) targets := $(pf)_1 else ifeq ($(user),user1) targets = $(pf)_2 else targets = $(pf)_3 endif $(targets):$(targets).c $(CC) $(targets) $(targets).c

In the above makefile, first variable “user” is compared with “root” then with “user1”. If both the conditions turn out to be false then the statements after the final else gets executed, which sets the targets to hello_3.c as required.

If we run the above makefile as root then we will get the output

cc -o hello_1 hello_1.c

If we run it as “user1” we will get the output

cc -o hello_2 hello_2.c

If we run it as any other user we will get the output

cc -o hello_3 hello_3.c

Category: Linux | Comments Off on makefiles: Complex conditional execution

Conditional execution using ifneq

Conditional execution can also be done using ifneq which is the opposite of ifeq.

The syntax of ifneq is

ifneq(,) statements else statement endif

ifneq(,) returns true when and are not equal. If and are equal it jumps to the else statement.

Using the above logic, the example used in the post “Conditional execution using ifeq” can be re written using ifneq as below.

CC = cc -o pf=hello user = $(shell whoami) ifneq ($(user),root) targets := $(pf)_1 else targets = $(pf)_2 endif $(targets):$(targets).c $(CC) $(targets) $(targets).c

Thus ifneq checks if the current user is not root, which if true then sets the target to $(pf)_1, else it sets it to $(pf)_2.

If we execute the make command as root we will get the following output

$ make cc -o hello_2 hello_2.c

If we do the same as any other user other than root, we will get the following output

$ make cc -o hello_2 hello_2.c

Category: Linux | Comments Off on Conditional execution using ifneq

Makefiles: Using variables

Prev: make is smart We can use variables in makefile to prevent typing the same strings over and over and also make the file more easy to understand.

Defining variables: Let us say we have a makefile to compile three independent executables hello_1, hello_2 and hello_3

all: hello_1 hello_2 hello_3 hello_1:hello_1.c cc -o hello_1 hello_1.c hello_2:hello_2.c cc -o hello_2 hello_2.c hello_3:hello_3.c cc -o hello_3 hello_3.c

In the above makefile we use the string “cc -o” repeatedly. Instead of typing the whole string every time we can create a variable for it.
We can create a variable of any name by using the “=” sign. The left hand would be the name of the variable and the right hand side would be the value of the variable.
Any where in the file if we want to use the value of the variable we just have to use the name of the variable in the format $() and make will automatically replace the variable with its value while executing.

CC = cc -o all: hello_1 hello_2 hello_3 hello_1:hello_1.c $(CC) hello_1 hello_1.c hello_2:hello_2.c $(CC) hello_2 hello_2.c hello_3:hello_3.c $(CC) hello_3 hello_3.c

In the above file have created a variable CC and used it whenever we needed cc -o.
Note that variables are case sensitive thus “cc” “CC” and “Cc” are all different.
We can also use variables for prefixes of various strings. Like in the example above “hello” is like a prefix to a number of strings, thus we can use a variable for it.

CC = cc -o pf=hello all: $(pf)_1 $(pf)_2 $(pf)_3 $(pf)_1:$(pf)_1.c $(CC) $(pf)_1 $(pf)_1.c $(pf)_2:hello_2.c $(CC) $(pf)_2 $(pf)_2.c $(pf)_3:hello_3.c $(CC) $(pf)_3 $(pf)_3.c

Recursive expanded variables : We can create recursive variables by using one variable while declaring another.

CC = cc -o pf=hello targets = $(pf)_1 $(pf)_2 $(pf)_3 all: $(targets) $(pf)_1:$(pf)_1.c $(CC) $(pf)_1 $(pf)_1.c $(pf)_2:hello_2.c $(CC) $(pf)_2 $(pf)_2.c $(pf)_3:hello_3.c $(CC) $(pf)_3 $(pf)_3.c

In the above example “targets” is a variable, which is a list of all the targets. The targets variable uses the variable “pf” , thus to find out what $(targets) refers to make will have to expand pf creating a recursion among variables any depth of such recursion is allowed.
The limitation of defining variables using “=” is that a variable can not refer to itself in recursion.

CC = cc -o pf=hello targets = $(pf)_1 $(pf)_2 targets = $(targets) $(pf)_3 all: $(targets) $(pf)_1:$(pf)_1.c $(CC) $(pf)_1 $(pf)_1.c $(pf)_2:hello_2.c $(CC) $(pf)_2 $(pf)_2.c $(pf)_3:hello_3.c $(CC) $(pf)_3 $(pf)_3.c

In the above example, the second definition of variable “targets” uses the “targets” itself in recursion. Such usage will result in an infinite loop, which make successfully detects and throws the error.

makefile:5: *** Recursive variable `targets’ references itself (eventually). Stop.

Simply expanded variables This can be prevented by making use of simply expanded variables which are defined using “:=”. The use of “:=” makes the expansion of all the variables in the assignment only once and stop, thus preventing the infinite loop.

Thus in the above example the second definition should be

CC = cc -o pf=hello targets = $(pf)_1 $(pf)_2 targets := $(targets) $(pf)_3 all: $(targets) $(pf)_1:$(pf)_1.c $(CC) $(pf)_1 $(pf)_1.c $(pf)_2:hello_2.c $(CC) $(pf)_2 $(pf)_2.c $(pf)_3:hello_3.c $(CC) $(pf)_3 $(pf)_3.c

Now make will not throw any error and will successfully compile the targets.

Using shell in declaration : We can also execute shell commands while defining variables.

CC = cc -o pf=hello targets = $(pf)_1 $(pf)_2 targets := $(targets) $(pf)_3 opd = $(shell pwd) all: $(targets) $(pf)_1:$(pf)_1.c $(CC) $(opd)/$(pf)_1 $(pf)_1.c $(pf)_2:hello_2.c $(CC) $(pf)_2 $(pf)_2.c $(pf)_3:hello_3.c $(CC) $(pf)_3 $(pf)_3.c

In the above example we executed the command “pwd” and assigned the result to the variable “opd” which we used while compiling to indicate the path where the compiled executable needs to be stored.

Category: Linux | Comments Off on Makefiles: Using variables