Makefile in linux

We offer you a brighter future with industry-ready online courses - Start Now!!

By the end of this article, you will what linux makefile is, why it is used, look at the alternatives of makefile, version, and types of make, and only then go to the practical part.

The fun part begins when we start looking at how to use a makefile. We will cover most of the ground as we discuss variables, conditional loops like if-else, functions like forereach function, call function, shell function and so many more coll topics. So let’s start!!!

What is a makefile and why do we use it?

Makefile is a command-line-based utility in Linux-based operating systems that helps us in deciding which part of gargantuan programs needs to be recompiled. It comes in handy when you want to run or update a task when certain files are updated.

The make file tool requires a file that either goes by the name “Makefile” or “makefile” with a set of tasks that need to be executed. Most open-source projects use ‘make’ to compile a final executable program which can later be installed by using “make install”.

In this tutorial, we will be prioritizing C and C++ as in the majority of the cases these programs are compiled as other languages have their own tools.

Are there alternatives to Make?

Of course, there are! Take C/C++ for example, they have alternate build systems like Scons, CMake, Bazel, Ninja, and more. Other code editors like VS Code (Visual Studio Code) have their own in-built tools.

Java also has its own tools, namely Ant, Maven, and Gradle. However, programming languages like Python, Ruby, and javascript don’t require analog Makefiles.

Versions of Makefile

There are various implementations of Make, but don’t worry, most of this tutorial will work on any version of Make. All the examples shown in this article work on all the versions 3 and 4. The version of Makefile I am using currently is 4.2.1.

If you want to see the version of your Make, simply type the command “make -version” in the terminal.

version of makefile

How does Make work?

The main goal of Makefile is to compile the files of your code that need to be compiled. It does this based on what files have changed. But when the files in the interpreted programming language change, nothing needs to be recompiled. You must keep in mind that when the program runs, the most recent version of the file is used.

Here is a diagram showing the example dependency graph that you might build with Make. You must not that the file will get recompiled If any file’s dependencies change:

how does makefile work

Syntax of the makefile command in linux

Every MakeFile consists of a set of rules, and a rule looks like this:

<targets>: <prerequisite>
<command>
<command>
<command>

Let us take a closer look at the fields in the syntax of writing a rule:

1. <targets>

The targets are nothing but file names, separated by spaces. Mostly, there is only one per rule.

2. <prerequisite>

The prerequisite file is also file names, separated by spaces. However, you need to keep in mind that the files you specified need to exist before the commands for the target are run. Prerequisites are also called dependencies.

3. <commands>

The commands are a set of actions or a series of steps typically used to make the target(s). You must always bear in mind that commands always start with a tab character and not spaces. If at all you give spaces instead of a tab, make will fail.

Printing “Hello World”

Before we jump into the action of Make, let us print the cliched “hello world” statement to understand how to write make and execute them. First of all, you need to have “Make” installed in your system, if you don’t you can simply use the command “sudo apt install make”.

We need a file to write the set of instructions right, well, there are countless ways of creating it, you can use, nano, vim, vi, gedit, cat and so many more, the only thing that is important is that the name of the file must either be “Makefile” or “makefile”. Once you create the file, write the following piece of code in it:

code for printing Hello world

Once you have written it, save and close it. To execute it, simply type the command “make” in the terminal, and you will get the following output:

printing hello world

Beginner examples

Before we actually head-on and understand variables, conditionals, functions, and more, let us get our basics straight and understand how targets, prerequisites, and commands work by looking at some simple and small examples.

Let us create a Makefile that has 3 separate rules, when you run “make blah” in the terminal, it will build a program called “blah” in the following series of steps:

1. Since make is given “blah” as the target, it searched for this target

2. “blah” requires “blah.o”, so it searched for “blah.o”

3. “blah.o” requires “blah.c”. co it searches for this target.

4. However, “blah.c” has o dependencies, so it runs the “echo” command

5. Then the “cc -c” command is run as all of the “blah.o” dependencies are finished

6. Then the “cc” command as all of the “blah” dependencies are finished

7. And voila! “Blah” is now a fully compiled C program.

example to understanding how make files works

And here is the output, executed just like we anticipated!!

output of blah

Let us look at a few more examples before proceeding further.

In the below-shown makefile, we only have a single target called “a_file”, so the default target is the first target, so the “a_file” will run.

another example to understanding how make files works

And when we execute the Makefile, we will get the following output:

prints the line everytime

In the below-shown makefile, all we did is add a touch command to create “a_file”.

adding touch command to the program

And we get the output as expected nothing special, we also created the file “a_file”, which we can see by running the “ls” command

creates a file when executed the first time

But the special part is when you run the program again, Make is clever enough to not create another “a_file” but tells you that it is already created and up to date!

updates the file instead of creating it when run after the first time

If we now make this program slightly more complex by depending the target “a_file” on “b_file”, the target will first look at its list of dependencies, and if any of them are older, it will first run the targets for those dependencies, and then run itself. However, the second time you execute this program, neither target will run because both targets exist.

code to create 2 files

So in the command shown below, first the target “b_file” will run followed by “a_file”. Anagin you can check if the file “b_file” is successfully created or not by using the ls command.

checking if program created 2 files

Now that we got the hang of how Makefiles work, and how the program executes, let us dive deeper and explore things like targste, variables, conditional statements, functions and more.

Targets

If you want to make multiple target and run all of them, you can simply make an all target

code useing targets

output of the code using targets

 

Multiple targets

When there are multiple targets for a rule, the commands will be run for each target specified. We can use the “$@” which is an automatic variable that contains the target name. Here is an example:

code using multiple targets

output of the code using mutiple targets

 

Infact, the above shown program is the same as the program shown below:

alternate way to write mutiple targets

And we even get the same output:

output of the code using mutiple targets

Variables

In Make variables can only be strings, they can’t be integer, float. Booleans, etc, just plain strings. You typically use, “:=” but “=” also works. Let us look at an example to get a better understanding:

code showcasing the use of variables

Here is the output of the program shown in the above screenshot:

output of the code showcasing the use of variables

Reference variables on the other hand use “${}” or “$()”, here is an example

code using reference variables

And the output for the above shown code:

output of the code using reference variables

 

Flavors and modification

In make, there are 2 flavor variables:

  • = (recursive) – It only looks for the variables when the command is used
  • := (simply expanded) – only those defined so far get expanded

Here is an examples using both the Recursive and simply expanded variables:

another example of a code using reference variables

 

And the output of the code shown above:

output of the above code using reference variable

The simply expanded variable allows you to append a variable as shown:

code using the simply expanded variable

Here we appended “hello” (as a variable) to “there” as seen below:

output of the code using the simply expanded variable

Another variable is “?=”, it only sets variables if they have not yet been set as shown in the below example:

code to sets variables if they have not yet been set

output of the code that sets variables if they have not yet been set

If you think of giving spaces to your variables, you can do so, However, Spaces at the end of a line are not stripped, but those at the start are. Therefore, if you want to give spaces in a proper and professional way, create a variables called “$(nullstring)”

code using the nullstring variable

As you can see in every place we put the null string variables we get a blank space in the output as shown below:

code using the nullstring variable output of the code using the nullstring variable

If at all you are wondering what happens if we don’t define a variable? The answer is that they will simply be an empty string.

code where we dont define a variable

As you can see even though we used the variables “nowhere”, we did not define it, but it still acts as an empty string.

output of the code using empty string

Command-line arguments and override

You can actually override the variables that come from the command line by using override. Here is an example:

code showcasing command line arguments and override

output of the code showcasing command line arguments and override

List of commands and “define”

“Define” is actually just a list of commands, don’t confuse it with a function. Here is an example:

code showcasing how define works

output of the code showcasing how define works

Target specific variables

You can also assign variables for specific targets as shown below:

code using target specific variables

output of the code using target specific variables

Pattern specific variables

Variables can also be set for specific patterns as shown below:

code using pattern specific variables

output of the code using pattern specific variables

Automatic variables

There are many automatic variables that we can use in makefiles, but only a few showup.

code using automatic variables

output of the code using automatic variables

Wildcards

In make files we can use either “*” or “%” as wildcards, but they mean entirely different things. Let’s look at the difference between th 2.

1. *

This wildcard searches your filesystem for matching filenames

code using the astriesk wildcard

2. %

This wildcard is used for the following tasks:

a. matches one or more characters in a string. This match is called the stem.

b. takes the stem that was matched and replaces that in a string.

c. used in rule definitions and in some specific functions.

Fancy rules

In Make files we have the following fancy rules

1. Implicit rules

2. Static pattern rules

3. Static pattern rules and filter

4. Pattern rules

5. Double-colon Rules

Let us look at each one in detail along with an example:

Implicit rules

When make is compiling c programs, it uses a automatic rules, make calls these “implicit rules” here is a list of the Implicit rules:

Compiling a C program:

“n.o” is made automatically from “n.c” with a command of the form $(CC) -c $(CPPFLAGS) $(CFLAGS)

Compiling a C++ program

n.o is made automatically from n.cc or n.cpp with a command of the form $(CXX) -c $(CPPFLAGS) $(CXXFLAGS)

Linking a single object file

n is made automatically from n.o by running the command $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)

Here is an example to build a C program without explicitly telling Make how to do the compilation:

example of a code to build a C program without explicitly telling Make how to do the compilation

Static pattern rules

Static [pattern rules is another way to write less in a Makefile!the syntax we use is shown below:

targets...: target-pattern: prereq-patterns ...
commands

The basic idea is that the given target is matched by the target-pattern (through a % wildcard). Here is an example:

code using static pattern rules

Static pattern rules and filters

The filter can be used in static pattern rules to match the correct files. In the example shown below, I made up the .raw and .result extensions.

code using static pattern rules and filters

Pattern rules

Pattern rules are best used for

1. Defining your own implicit rules

code using pattern rules for defining your own implicit rules

2. Used as a simpler form of static pattern rule.

code to use pattern rules as a simpler form of static pattern rule

Double – colon rules

Double colon rules are very seldom used. These rules allow multiple rules to be defined for the same target. You must be careful while using them and not get confused with single colons, as a warning would be printed and only the second set of commands would run.

code using double colon rules

output of the code using double colon rules

Commands and execution

Command echoing/silencing

To stop a command from being printed, use the at the rate (@) symbol before the command. You can also run make with “-s “to add an at the rate symbol (@) before each line.

code showcasing command silencing

output of the code showcasing command silencing

Command execution

Each command runs in a new shell

code showcasing command execution

output of the code showcasing command execution

Default shell

Speaking of shell, every makefile’s default shell is the /bin/sh however, you can change this by using the variable “SHELL” at the beginning of the program as shown below:

code to change the default shell

output of the code to change the default shell

Error handling

We handle different errors by using the following characters:

1. -k

We can use “-k” when running make to continue running even if it faces errors.

2. –

We can use “-” before a command to suppress the error

3. -i

We can use “-i” to make to have this happen for every command

code to handle errors by using the option i

output of the code using the option i to handle errors

Interrupting or Killing make

You can simply interrupt or kill make in the middle of something by pressing the key combination “ctrl” + “c”. It will also delete the newer targets it just made.

Recursively using make

If you want to recursively call a makefile, use the special variable “$(MAKE)” instead of “make”. Why? Because it will pass the make flag for you and won’t affect itself by them. Here is an example:

recursively using make

output of recursively using make

Using export for recursively calling a makefile

Another way to recursively call a makefile is by using export. The export directive takes a variable and makes it accessible to the command present in make, or most commonly called sub make commands.

code using export for recursively calling a makefile

output of the code using export for recursively calling a makefile

If-else statement in Makefiles

We all know how the if-else statement works, the logic behind it is the same, the only thing that changes between various programming languages is the syntax. Let us look at one example to get a better idea of if-else statements in Makefiles:

code showcasing the use of if else statement

output of the code showcasing the use of if else statement

The above program checks if the string is “DataFlair” or not. Let us now use this conditional statement in a few examples.

Checking if a variable is empty or not

code to check if a variable is empty or not

output of the code to check if a variable is empty or not

Functions in Makefiles

In make there are the following function:

1. First functions

2. String substitution

3. Foreach function

4. If function

5. Call function

6. Shell function

Let us dive deeper and understand each one of them with the help of an example:

First functions

The primary purpose of functions is text processing, you can also make your own functions using the call built in function.

code using first functions

output of the code using first functions

String substitution

The string substitution function finds whitespace separated word in the text that match pattern and replaces them with the replacement you specified.

code using string substitution

output of the code using string substitution

Foreach function

The foreach function converts one list of words to another. It looks something like this:

$(foreach var, <list>, <text>)

code using foreach function

output of the code using foreach function

If function

The if function checks if the first argument is non-empty or empty. If at all the first argument is not empty, it runs the second argument, otherwise the third.

code using the if function

output of the code using if function

Call function

As we glimpsed earlier, the inbuilt call function supports creating basic custom functions. It is you who defined the function just by creating a variable. You will have to use the parameters $(0), $(1), etc. The syntax you use to create your own function is:

$(call <variable>, <parameter>, <parameter>)

code using the call function

output of the code using the call function

Shell function

The shell function calls the shell, but it replaces the newlines with spaces.

code using the shell function

output of the code using shell function

Bonus makefile features

Before we call it a day, let us look at some bonus features of makefile.

Multiline

The backslash (\) gives us the ability to use multiple lines when the commands are too long. Here is an example:

code using the multiline feature of makefile

.phony

We can add “.PHONY” to a target to prevent make from confusing the phony target with a file name. In the example shown below, if the file “clean” is created, make clean will still be run.

code using the phony feature of makefile

.delete_on_error

Make stop running a rule if a command returns a nonzero exit status. This is where “.delete_on_error” comes in handy. It deletes the target of a rule if the rule fails in this manner. This happens for all targets, not just the one it is before like PHONY

code using the delete on error feature of makefile

Summary

We have now covered all of the basics and intermediate level practical commands related to make. As you have seen, makefile is a simple compiler of various programming languages.

You have now learned what makefile is, why it is used, the alternatives of makefile, how makefile works, and the syntax. In the practical part we kicked of by printing “hello world” and then going through a few beginner examples, we then went deep and understood things like targets, wildcats, variables, rules, commands and execution, if-else statement, functions and a few bonus features like PHONE, multiline and delete on error.

Did you know we work 24x7 to provide you best tutorials
Please encourage us - write a review on Google

courses

DataFlair Team

DataFlair Team is a group of passionate educators and industry experts dedicated to providing high-quality online learning resources on programming, Java, Python, C++, DSA, AI, ML, data Science, Android, Flutter, MERN, Web Development, and technology. With years of experience in the field, the team aims to simplify complex topics and help learners advance their careers. At DataFlair, we believe in empowering students and professionals with the knowledge and skills needed to thrive in today’s fast-paced tech industry. Follow us for Free courses, expert insights, tutorials, and practical tips to boost your learning journey.

Leave a Reply

Your email address will not be published. Required fields are marked *