Makefile

Wildcards and Foreach in Makefile

A file is always made up of a different type of content: whether it’s a simple text file, a program code file, or any makefile. The content type of any file makes it unique and differentiates it from the other format of files. Just like that, makefile consists of rules that use different elements to perform its work properly. From those elements, there is the wildcard and foreach which are required to perform something unique and extra. Within this guide, we will discuss the power of wildcards and foreach while they are used in a makefile.

Wildcards in Makefile

In general, the wildcard is known to be a factor that cannot be predicted and can turn the situation in any way possible. The wildcards in the makefile are used to perform a bonus of detecting all the possible patterns from a current working directory, whether it’s a file or any folder. These source files can be of any type.

To use a wildcard in a makefile, you should use the “wildcard” keyword that follows the asterisk “*” or “?” sign and the file extension that is connected through a dot. You can also utilize the “?” sign to search for a single character and “*” to match any number of characters. This whole structure should be utilized within the brackets and a “$” sign. For instance, we declare the “SRCS” variable that gets its value file via the wildcard. This wildcard looks for all the files with the “cpp” pattern at their end.

Foreach in Makefile

The foreach function of makefile works exactly like a foreach loop in programming languages – iterate over the items in a list. The foreach function in makefile performs a particular action on each item of a list. This element can be a variable or any source file. For instance, we elaborate the syntax of the foreach function in makefile via the SOURCES variable that contains a list of three source files. The foreach function uses this SOURCES variable to create the same name for three object files by iterating the list of source files and saving them to another “OBJECTS” variable. The last two lines show how a makefile rule can be used to create an object file for each C file after iterating.

SOURCES := file1.c file2.c file3.c
OBJECTS := $(foreach src,$(SOURCES),$(src:.c=.o))
$OBJECTS : %.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

Example 1: Using Wildcards

To display the illustration and working of wildcards and foreach functions, we introduce a C++ file. This “salary.cpp” file is started with the use of the “iostream” header to allow a smooth use of input and output streams. The main method declares a variable “s” of integer type and the “cout” statement to ask for an input value at runtime. The “cin” standard input stream gets the value from a user at runtime and saves it into the variable “s”. The “cout” displays the inputted value by a user on the console screen.

#include <iostream>
using namespace std;
int main(){
    int s;
    cout << "Enter Salary: ";
    cin >> s;
    cout<<"\nSalary: "<<s <<endl;
    return 0;
}

We start the makefile with the “CXX” variable which defines the compiler for C++, and the CXXFLAGS variable holds the flags for the C++ compiler. The EXECUTABLE variable holds the name of an executable “hello” file that is generated after the execution of a makefile. The SRCS variable gets all the C++ files from a current directory using the wildcard “*” to search any pattern that ends with “.cpp”. The OBJS variable holds the names of object files to be created using the SRCS variable, replacing the “cpp” extension with “o”. The default “all” target builds the makefile and it depends on the EXECUTABLE variable.

The first rule creates the target “hello” file that depends on the OBJS variable (names of object files) object file using the file names that are generated via the “OBJS” variable. The second makefile rule generates the object file with the “.o” extension that depends on the C++ file after compiling the C++ code file. Here, “%” is a wildcard to search for the filenames of any pattern that ends with “cpp”. In the end, the clean target uses its “rm” command to forcefully clean the newly generated executable and object files from a directory using the “-f” flag.

CXX = g++
CXXFLAGS = -Wall -std=c++11
EXECUTABLE = hello
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
all: $(EXECUTABLE)
$(EXECUTABLE): $(OBJS)
    $(CXX) $(CXXFLAGS) -o $@ $(OBJS)
%.o: %.cpp
    $(CXX) $(CXXFLAGS) -c $< -o $@
clean:
    rm -f $(EXECUTABLE) $(OBJS)

Upon running the “make” instruction, both the target and object files were created. After executing the executable “hello” file, the user is asked to enter a salary and we add “67000”. At the end, the salary is displayed back.

make

Example 2: Using Foreach

After using wildcards, it’s time to make use of the foreach function in makefile. The rest of the makefile code is the same. On line 6, we initialize another variable which is “NAMES” with a list of three values – Kate, Kim, Tim. The default “all” target depends on the EXECUTABLE variable (target file name “hello”) and foreach statement. The “addprefix” function iterates the “NAMES” variable to dynamically generate the target names by prefixing “run_” at the start of every name in the “NAMES” list.

The rule at the eight line indicates that an output executable target file, i.e. hello, is dependent on the “OBJS”. The “-o” flag generates the target output file using the OBJS. The rule at the tenth line generates the target object file using the source files with the “cpp” extension. To do so, the “-c” flag is utilized to compile a source file and generate the related object file that is necessary for the target generation. At the thirteenth line, we use the EXECUTABLE variable to generate the output with different names starting with “run_” as a prefix. In the end, the clean target and Phony targets will remove and clean the object and target files.

CXX = g++
CXXFLAGS = -Wall -std=c++11
# executable target file
EXECUTABLE = hello
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
# List of names
NAMES = Kate Kim Tim
# Targets
all: $(EXECUTABLE) $(addprefix run_, $(NAMES))
$(EXECUTABLE): $(OBJS)
    $(CXX) $(CXXFLAGS) -o $@ $(OBJS)
%.o: %.cpp
    $(CXX) $(CXXFLAGS) -c $< -o $@
# Create targets for each name
run_%: $(EXECUTABLE)
    ./$(EXECUTABLE) $*
clean:
    rm -f $(EXECUTABLE) $(OBJS)
# Phony targets
.PHONY: all clean

The use of the “make” instruction generates the executable “hello” target and runs the program for each name that is specified in the “NAMES” variable.

make

You can also modify your output using the name from a list with the “run_” prefix.

Make run_Kim

Conclusion

This guide discussed the use of wildcards and foreach concepts in the makefile while discussing their syntax separately. After that, we discussed the code examples to elaborate on each of their working with outputs on getting the files with the same extensions and iterating the values in a variable list.

About the author

Saeed Raza

Hello geeks! I am here to guide you about your tech-related issues. My expertise revolves around Linux, Databases & Programming. Additionally, I am practicing law in Pakistan. Cheers to all of you.