############ 3.5 Makefile ############ ==================== 1. What is ``make``? ==================== ``make`` is a tool which controls the generation of executables and other non-source files of a program from the program's source files. ======================== 2. What is ``Makefile``? ======================== A makefile is a file containing a set of directives used by a make build automation tool to generate a target/goal. ===================== 3. Makefile structure ===================== A makefile consists of a set of rules. Each rule consists of a target, a list of prerequisites, and a series of commands to be executed. The makefile consists of ``rules``. Each rule consists of ``target``, ``prerequisites`` and ``commands``: - The ``target`` is usually the name of a file that is generated by a program; examples of targets are executable or object files. A target can also be the name of an action to carry out, such as ``clean`` (see Phony Targets). - The ``prerequisites`` are files that are used as inputs to create the target. A target often depends on several files. - The ``commands`` are a series of steps typically used to convert the prerequisites into the target. A simple makefile consists of ``rules`` with the following shape: .. code-block:: bash target: prerequisites command command command The target is the name of the file that is generated by the command. The prerequisites are files that are used as input to create the target. The commands are a series of steps typically used to convert the prerequisites into the target. ============== 4. Hello World ============== Make sure that you have ``make`` installed on your system. If not, install it using the following command: .. code-block:: bash sudo apt-get install build-essential Now, let's create a simple Makefile that prints "Hello, World!" when you run make. Here's the content of the ``Makefile``: .. code-block:: bash hello: @echo "Hello, World!" Save the content above in a file named ``Makefile`` (it's case-sensitive; it should be exactly Makefile unless you want to specify the makefile name every time you run make). Now open a terminal, navigate to the directory where your Makefile is located, and simply type ``make``. The output should be "Hello, World!". .. note:: Explanation The word ``hello`` followed by a colon ``:`` specifies a "target". A target is essentially a label for a set of commands that are executed when you run make . ``@echo "Hello, World!"``: This line is a command that the make utility will execute. The ``@`` symbol prevents the command itself from being printed to the terminal before it's executed, so only the command's output ("Hello, World!") will be shown. Without the ``@``, you would see both the command and its output. Indentation matters: The commands to be executed for a target must be indented by a ``Tab``, not spaces. Comments start with ``#``, and they are ignored by make. To run the target hello, you'd just execute ``make hello`` or simply ``make`` (if hello is the first target in the Makefile, it becomes the default target). ========================== 5. Variables and Arguments ========================== Variables are used to make makefiles more flexible and reusable. Variables can be defined in the makefile, passed as arguments to ``make``, or inherited from the environment. Variables are defined in the makefile using ``VAR = value`` or ``VAR := value``. The difference between the two is that ``VAR = value`` is a ``recursive`` variable, while ``VAR := value`` is a ``simple`` variable. Recursive variables are more flexible than simple variables, but simple variables are more predictable. Recursive variables are defined by ``VAR = value``. The value of a recursive variable is only expanded when it is used. This means that you can define a recursive variable at the end of the makefile, and use it in variable definitions above that line. Simple variables are defined by ``VAR := value``. The value of a simple variable is expanded immediately. This means that you can't define a simple variable at the end of the makefile and use it in variable definitions above that line. Passing arguments directly to Makefile targets isn't straightforward because make wasn't designed to accept arguments in the way that a normal shell script does. However, there are workarounds to accomplish this. One common way is to use make variables. These can be set on the command line when you invoke make. .. code-block:: bash # This is a comment # The target is "hello" and the command to execute is below it hello: @echo "Hello, $(name)!" To run this with your name, you would enter something like: .. code-block:: bash make hello name=Ion The output would be "Hello, Ion!". .. note:: Explanation ``$(name)``: This is a variable in make syntax. When you run ``make hello name=Ion``, make assigns the value "Ion" to name, and then substitutes ``$(name)`` with "Ion" when it executes the echo command. You can also specify a default value for the variable within the Makefile: .. code-block:: bash name=World hello: @echo "Hello, $(name)!" With this version, if you just run ``make hello`` without specifying name, it will default to "World". If you run ``make hello name=Ion``, it will override the default value with "Ion". ================ 6. PHONY Targets ================ The ``.PHONY`` target in a Makefile is used to specify that a target name does not represent a file. In make, by default, the targets are file names. When you define a target, make checks the modification time of the files that the target depends on (if any), and the modification time of the target file itself, to decide whether it needs to execute the commands under that target. However, sometimes you have targets that don't produce any files or where the name of the target doesn't correspond to a file. In those cases, you use ``.PHONY`` to tell make that the target is not a file name. .. code-block:: bash .PHONY: hello clean hello: @echo "Hello, World!" clean: @rm -f *.log .. note:: Explanation ``.PHONY: hello clean``: This line tells make that hello and clean are phony targets, which means they don't represent files. Therefore, make will always execute the commands under these targets, even if there's a file named hello or clean in the directory. --------------------- Why is .PHONY useful? --------------------- - *Always Execute*: When a target is phony, make will always execute its commands, regardless of file states. - *Avoid File Name Conflict*: Imagine you have a file named clean in your directory. If you didn't specify clean as a phony target, make clean would do nothing, thinking that the target clean is up-to-date because a file with the same name exists. - *Clarifies Intent*: By using ``.PHONY``, you make it explicit that the target doesn't produce a file with the target's name. This makes the Makefile easier to understand. You can place ``.PHONY`` either at the top of your Makefile to declare all phony targets at once, or right before each phony target for better readability and maintainability, like so: .. code-block:: bash .PHONY: hello hello: @echo "Hello, World!" .PHONY: clean clean: @rm -f *.log