A Makefile is a special type of file used in software development primarily for automating the build process of a project.
It contains a set of instructions (rules) that define how the project's source code files should be compiled, linked, and assembled to generate the final executable or other output files.
Makefiles are commonly used in projects written in programming languages like C, C++, and similar compiled languages. They serve as a way to streamline the build process and manage dependencies between different source files. The Makefile specifies the relationships between source files and the commands needed to transform them into executable code or libraries.
Here's a basic overview of how a Makefile works:
1. **Targets:** A target is usually the name of the output file or action that needs to be performed. It could be an executable, a library, or even a simple action like cleaning up temporary files.
2. **Dependencies:** Each target can have one or more dependencies, which are the files or other targets that need to be processed before the target can be built. Dependencies help ensure that files are built in the correct order.
3. **Rules:** A rule specifies how to build a target based on its dependencies. It consists of a target, a colon, a list of dependencies, and a set of commands. The commands are typically shell commands that are executed to compile, link, or perform other necessary actions.
4. **Variables:** Makefiles often use variables to store values that are reused throughout the file. This can include compiler options, file paths, and other configuration settings.
5. **Comments:** Comments in a Makefile start with the "#" symbol and are ignored by the make utility.
The `make` utility is used to read the Makefile and execute the commands specified in the rules to build the targets. When you run `make` followed by a target name (e.g., `make all` or `make clean`), the utility checks the dependencies and decides which targets need to be rebuilt based on changes in source files.
Here's a simple example of a Makefile that compiles a C program named "my_program" from two source files, "main.c" and "helper.c":
In this example, running `make all` would compile "main.c" and "helper.c" using the specified compiler and flags, and produce the "my_program" executable. Running `make clean` would remove the generated executable.
They are particularly helpful in projects where there are multiple source code files, dependencies between these files, and various steps involved in building the final executable or output. Makefiles make it easier to manage complex build processes, ensure consistent compilation, and automate repetitive tasks.
When to Use Makefiles:
Large Projects: Makefiles are especially useful for large projects with many source files, where manually compiling each file would be time-consuming and error-prone.
Dependencies: When your project has interdependencies between source files (e.g., one file depends on the output of another), Makefiles help manage these relationships and ensure proper compilation order.
Automated Build Process: If you need to automate the process of compiling, linking, and generating the final executable, Makefiles provide a standardized way to do so.
Makefiles track changes in source files and recompile only the necessary files when changes are detected. This saves compilation time when working on larger projects
Rules:
Rules are the heart of the build process. Rules define how to transform source files (dependencies) into target files using specified commands.
A rule consists of three main components: targets, dependencies, and commands.

The symbols $<
and $@
:
special variables used in Makefiles. They have specific meanings within the context of a rule, and they refer to different parts of the rule. Here's what they represent:
$<
: This variable represents the first prerequisite (dependency) of the target in a rule. In other words, it refers to the source file that triggered the execution of the rule.$@
: This variable represents the target of the rule. It refers to the file that you are trying to build.
Explicit and implicit rules :
Two different approaches to specifying how to transform source files (dependencies) into target files using commands. They provide flexibility in defining build processes for your projects.
Explicit Rules:
Explicit rules are rules that explicitly define how to create a target from its dependencies. Each explicit rule has a target, dependencies, and associated commands.
Explicit rules are useful when you have specific requirements for how a target should be built, and you want to provide detailed instructions for each step.
Here's an example of an explicit rule in a Makefile:
prog: main.c helper.c
gcc -Wall -o prog main.c helper.c
In this explicit rule:
- `prog
` is the target.
- `main
.c` and `helper
.c` are the dependencies.
- The command `gcc -Wall -o prog main.c helper.c` is used to build the `prog` target from the dependencies.
Implicit Rules:
Implicit rules, on the other hand, are predefined rules that are used when there is no corresponding explicit rule. Implicit rules are used to generate targets automatically based on their file extensions. These rules are built into `make`, and they simplify the Makefile by allowing you to avoid specifying every rule explicitly.
For example, if you have a rule like this:
%.o: %.c
gcc -c $< -o $@
This is an implicit rule that says how to compile a `.c` file into a `.o` object file. The `%` is a pattern that matches any string. So, if you have a source file named `main.c`, `make` will use this implicit rule to generate the corresponding object file `main.o`.
When to Use Explicit and Implicit Rules:
- **Explicit Rules:** Use explicit rules when you want to provide precise instructions for how a target should be built. Explicit rules give you full control over the build process and are useful for complex or non-standard tasks.
- **Implicit Rules:** Implicit rules are convenient when you have a standard build process that can be inferred from the file extensions. They help keep your Makefile concise and automatically handle common tasks like compiling source files or generating object files.
In many cases, a combination of explicit and implicit rules is used in a Makefile. Explicit rules handle special cases or complex tasks, while implicit rules cover standard build steps.
What are variables and how to set and use them ?
Variables are used to store values that can be reused throughout the Makefile. Variables make it easier to manage and customize various aspects of the build process without repeating the same values in multiple places.
Setting Variables:
Variables in Makefiles are typically defined using the following syntax:
VARIABLE_NAME = value
Here's an example of setting variables in a Makefile:
CC = gcc
CFLAGS = -Wall
In this example:
CC
is a variable storing the compiler command (gcc
).CFLAGS
is a variable storing compiler flags (-Wall
for enabling warnings and optimization).
How to use variables ?
Once you've defined variables, you can use them by enclosing their names in parentheses preceded by the $
symbol. For example:
prog: main.c helper.c
$(CC) $(CFLAGS) -o prog main.c helper.c
In this example, $(CC)
and $(CFLAGS)
are replaced with their corresponding values (gcc
and -Wall
) when the rule is executed.
Default Variables:
Makefiles also have default variables provided by the make
utility. These variables can be useful to retrieve information about the environment, such as the name of the operating system, the version of the make
utility being used, and more. Some commonly used default variables include:
CC
: The C compiler command (gcc
by default).CFLAGS
: Flags for the C compiler (-g -O2
by default).LDLIBS
: Libraries to link with (-lm
by default for math library).RM
: The command used to remove files (rm -f
by default).
How to make The all rule should recompile only the updated source files?
Pattern Substitution : syntax:
$(patsubst %.c, %.o, $(SRC))
also known as string substitution, is a concept in programming and scripting languages that involves replacing a specified pattern within a string with another string. In the context of Makefiles, pattern substitution is used to manipulate filenames or strings based on specific patterns.
In Makefiles, pattern substitution is commonly used to transform a list of filenames or strings to achieve a specific purpose. This can include renaming files, changing file extensions, or generating lists of related filenames.
Example 1 - Changing Extensions:
FILES = file1.c file2.c file3.c OBJ = $(FILES:.c=.o)
In this example, the .c
extension in each filename in the FILES
list is replaced with .o
to generate the a
list containing corresponding object file names.
Example 2 - Adding Prefixes:
FILES = main.c helper.c OBJS = $(FILES:%=obj/%)
In this example, the %
symbol is a placeholder for each value in the FILES
list. The pattern obj/%
adds the prefix obj/
to each filename in the FILES
list to generate the OBJS
list containing filenames with the prefix.
Example 3 - Removing Prefixes:
ALL_FILES = src/main.c src/utils.c
BASE_FILES = $(ALL_FILES:src/%=%)
In this example, the pattern src/%
matches the prefix src/
in each value of ALL_FILES
. The %
symbol after the colon removes the prefix, resulting in the BASE_FILES
list containing filenames without the src/
prefix.
Pattern substitution in Makefiles provides a powerful way to generate new variables or lists of values based on existing ones. It's particularly useful when working with filenames or strings that follow a specific pattern.
Note:
*~
: This is a file pattern that represents backup files typically generated by text editors. Many text editors create temporary backup files by appending a tilde (~
) to the end of the filename. For example, if you have a file named main.c
, the text editor might create a backup file named main.c~
. Adding *~
to your Makefile helps exclude these backup files from being considered as source files.
Resources :