 Learn about Linux in practice  1. Introduction
In Linux, Makefiles are powerful tools for automating build processes, but their syntax can sometimes be tricky, especially when it comes to conditional statements. The syntax differs significantly from traditional programming languages, leading to confusion and errors. A common issue is attempting to use if-else statements directly within recipe lines, which doesn’t work as expected.
In this tutorial, we’ll explore the syntax and examples of using if-else blocks in Makefiles.
2. Conditional Directives in Makefiles
We use several kinds of conditional directives in a Makefile:
- ifeq – tests if two strings are equal
- ifneq – tests if two strings aren’t equal
- ifdef – the code tests if the variable definition is available
- ifndef – the condition tests if a variable definition isn’t available
We’ll explore examples of the above conditionals in the subsequent sections.
3. Basic Syntax
Let’s take a look at the basic syntax for an if-else statement in a Makefile:
ifeq (condition)
# Commands if condition is true
else
# Commands if condition is false
endif
The above snippet is a basic skeleton on the conditional statements in a Makefile. We’ll explore the above directives using various examples.
3.1. Testing if Two Strings Are Equal
The ifeq directive tests if two strings are equal:
MODE = debug
ifeq ($(MODE),debug)
CFLAGS = -g -O0 -DDEBUG
else
CFLAGS = -O2
endif
In this example, if MODE equals “debug”, then CFLAGS takes on debugging flags; otherwise, it takes on optimization flags.
3.2. Testing if Two Strings Aren’t Equal
The ifneq directive tests if two strings aren’t equal to each other:
OS = linux
ifneq ($(OS),windows)
RM = rm -f
else
RM = del
endif
Here, if OS isn’t “windows”, the RM command takes the Unix version; otherwise, it takes the Windows command.
3.3. Testing if a Variable Is Defined
The ifdef directive tests whether or not a variable is defined:
ifdef DEBUG
CFLAGS += -g -DDEBUG
endif
In this example, if DEBUG is defined (regardless of its value), the directive adds debugging flags to CFLAGS.
3.4. Testing if a Variable Isn’t Defined
The ifndef directive tests if a variable isn’t defined:
ifndef CC
CC = gcc
endif
In this case, if the CC variable isn’t defined, the Makefile assigns “gcc” as its default value. We commonly use this pattern in Makefiles to provide default values that users can override.
4. Makefile Examples
In the following sections, we’ll explore examples that combine various aspects of the above conditionals into a Makefile.
4.1. Checking the Operating System
Here’s an example that demonstrates the usage of conditional statements for handling different operating systems:
# Define the default target
all: hello
# Check the operating system
ifeq ($(OS),Windows_NT)
DETECTED_OS := Windows
else
DETECTED_OS := $(shell uname -s)
endif
# Target that uses the detected OS
hello:
@echo "Hello from $(DETECTED_OS)"
@echo "This is a Make example"
# Conditional target behavior
ifeq ($(DETECTED_OS),Windows)
install:
@echo "Installing on Windows..."
else ifeq ($(DETECTED_OS),Darwin)
install:
@echo "Installing on macOS..."
else
install:
@echo "Installing on Linux or other OS..."
endif
In the above example, we initialize the the variable DETECTED_OS based on environment variable OS. The details of the above steps are:
- ifeq ($(OS),Windows_NT) checks if the environment variable OS equals “Windows_NT” (which is automatically set on Windows systems).
- If true, it sets DETECTED_OS to “Windows”.
- Otherwise, it runs the Unix command uname -s (which returns the kernel name) and assigns that output to DETECTED_OS.
- On macOS, uname -s returns “Darwin”; while on Linux, it returns “Linux”.
Subsequently, we use the variable DETECTED_OS for installing the appropriate software based on the operating system.
4.2. Conditional Expressions
If we need conditional logic within a recipe, we use shell conditionals:
check_file:
@if [ -f "somefile.txt" ]; then \
echo "File exists"; \
else \
echo "File does not exist"; \
fi
Let’s break down the above code:
- check_file defines a target named “check_file”.
- @ at the beginning suppresses Make from printing the command before executing it (for cleaner output).
- if [ -f “somefile.txt” ]; then \ starts a shell conditional statement.
- echo “File exists”; prints “File exists” if the file exists.
- echo “File does not exist” prints “File does not exist” if the file doesn’t exist.
The entire command is actually a single shell command (a Bash if-statement). In a proper Makefile, this would typically be formatted with proper indentation for readability. In above example, we’re executing the command prefixed with @ in a Bash subshell.
4.3. Inline Conditionals
We can also use if conditionals for inline functions:
# Using $(if) function
MESSAGE := $(if $(DEBUG),Debugging mode, Release mode)
# Using computed variable names
MODE := debug
COMPILER_$(MODE) := gcc -g
COMPILER_release := gcc -O2
compile:
@echo "Using compiler: $(COMPILER_$(MODE))"
The above example illustrates inline functions using $(if …) syntax:
- If the DEBUG variable is defined, the Makefile sets the MESSAGE variable to “Debugging mode”
- Set the MESSAGE variable to “Release mode” if variable DEBUG isn’t set
We also use a computed variable based on the value of the MODE variable. This allows us to initialize the appropriate compiler parameters and use it for compiling the program.
4.4. Using the shell Command
We can combine the shell command in Makefile to check the required disk space:
# define the minimum disk space required
MIN_SPACE_MB=1000
valid-env:
@echo "validating build env ..."
ifeq ($(shell if [ $$(df -m . | tail -1 | awk '{print $$4}') -lt $(MIN_SPACE_MB) ];then echo 1; else echo 0; fi),1)
$(error Insufficient disk space. At least $(MIN_SPACE_MB)MB required)
endif
The above example illustrates a complex example of checking the environment for available disk space. The steps are:
- Sset the MIN_SPACE_MB variable to “1000” assuming the required disk space is 1000 MB
- The command df -m . | tail -1 | awk ‘{print $$4} evaluates the disk space on the current file system
- Compare the output of the above command with the expected disk size (MIN_SPACE_MB) and echo the result of 0 or 1
- We compare the result with 1 to determine if disk space is available
- In case of error, the $(error …) block prints the error
One important idea of note is that in Makefiles, we can’t directly split a shell command across multiple lines. A separate shell instance typically executes every recipe. As a result, the variables defined in one line aren’t available in the next. This leads to variable scope limitations.
5. Conclusion
In this article, we learned various ways of using if-else blocks in a Makefile. Understanding conditional statements in Makefiles is essential for creating versatile and maintainable build systems. By properly implementing if-else logic in our Makefiles, we can ensure our build processes are robust and flexible across different platforms and configurations. The post Using if-else Conditionals in a Makefile first appeared on Baeldung on Linux.
Content mobilized by FeedBlitz RSS Services, the premium FeedBurner alternative. |