A project has innumerable source files that are organized along various paths based on type, function, and module. The rules in the makefile state which files should be rebuilt, which files should be built later, and which files should be constructed first. The makefile prefers a shell with sophisticated functions. [^1][^2]
1. grammer
1.1 basic
object:dependence
<TAB> command line
The object is the target to be compiled or an action.
The dependence is a pre-item performing the object depended.
One object allows having multiple dependencies.
The command is the specific command under the performing object.
Each command occupies one line.
By default, the first object will be executed when no option is specified.
By default, make command search GUNmakefilemakefilesMakefile in current directory using as the make input file, except for that use option-f to specify a make file by filename.
-f: specify a make file by file path
-v: show the version number
-n: perform the makefile, output commands record only, don't perform.
-s: perform the makefile, don't show commands record.
-w: show paths before and after execution.
-C: show paths where the makefile is located.
1.3 using a common include make
We can use a makefile as a public makefile liking as the function #include in C language.
TARGET = z
OBJ = a.o
include ../makefile.common
Note, the = and :=:
= : the final value assignment, whatever the calling before the assigning value or behind the assigning value.
:=
There is a example for describing the difference between = and :=:
#### example 1:
a = 123
b = $(a)
a:
echo $(a) $(b) $(c)
a = 456
c = 789
#### example 2:
x = 789
y = $(x)
y = $(y)
b:
echo $(x) $(y)
The make aoutput is 456 456 789, and the make b output is an error, becasue the object b cannot ensure the y value that referenced itself.
Now, we replaced the = to :=:
#### example 1:
a = 123
b := $(a)
a:
echo $(a) $(b) $(c)
a = 456
c = 789
#### example 2:
x = 789
y = $(x)
y := $(y)
b:
echo $(x) $(y)
The make a output is 456 123 789, and the make b is no longer an error, rather then 789 789.
A = 123
B = default
C := $(B)
ifeq ($(A), 123)
B := yes
else
B := no
endif
ifneq ($(A), 123)
C := yes
else
C := no
endif
show:
echo $(A)
echo $(B)
echo $(C)
ifdef A
@echo "ifdef A : the A defined"
endif
ifndef A
@echo "ifndef A: THE A not defined"
endif
This is always combined gcc -D with ifdef. Sometimes, we want to use some variables of the makefile in a C source file, different variables different flows, such as using the debug, and setting the version number. Using the gcc -D is equal to defining a macro by #define.
version=0.0.2
release_number=2
all: test
test: test.c
$(CC) -o $@ $^ -DDEBUG_PRINT -D VERSION='"$(version)"' -D RELEASE_NUMBER=$(release_number)
.PHONY: clean
clean:
rm test
And the test.c is :
#include <stdio.h>
int main(int argc, char **argv) {
if (DEBUG_PRINT) {
printf("%s (version: " VERSION ", release number: %d)\n",
argv[0], RELEASE_NUMBER);
}
return 0;
}
So, we can utilize the ifdef of makefile and -D of gcc to do something.
1.7 using the foreach
In a makefile, the foreach can provide the loop function for us. We can make use of the foreach to create a directory or something.
It has a very basic makefile and appears quite silly. All the objects would be recompiled when we altered a single file. If the project is particularly large, compiling will take a lot of your time.
So we need to optimize the makefile and make it easier and more convenient.
When one of these files changed, the optimized makefile would re-compile the changed .c file.
2.1 using user env
Based on the simple makefile, we replaced *.o files with OBJ by user environment. The difference between the simple makefile and the new makefile is shown in the following figure:
2.2 using sys env
$*: the object name that does not include the extension name.
$+: all the dependence files, seperated by space.
$<: the first condition in rules.
$?: the dependence files that the time later than object.
$^: all the dependence files are not repeated.
$%: if the object is an archive member, the variable indicates the member name of the object.
Based on the user env makefile, we replaced OBJ and TARGET with $^ and ^@ by the system environment.
We can make a static library compiling within a makefile. Compared with a dynamic library, the static library needs not original library files during the application running, in other words, the static library has been inserted into the object file, which leads to the application being bigger than the one compiled by the dynamic library. For a static library generation, we can refer to section 3 of # 03_ELF文件_静态链接. The common commands summary is as follows:
As we compile a static library:
gcc -c hello.c -o hello.o
ar -r libhello.a hello.o
As we use a static library:
gcc -lhello -L./ main.c -o main.elf
Example:
Touch a mod.c
#include "mod.h"
int mod(int a, int b) {
return a % b;
}
Prevent the make command from stopping when the script runs into an error. If the make command captured the non-zero return value, the make script would halt and return this error. However, some errors in the process returned are meaningless. We can enable the function of make ignoring errors in your makefile script.
01_Makefile_makefile_summary
A project has innumerable source files that are organized along various paths based on type, function, and module. The rules in the makefile state which files should be rebuilt, which files should be built later, and which files should be constructed first. The makefile prefers a shell with sophisticated functions. [^1][^2]
1. grammer
1.1 basic
object
is the target to be compiled or an action.dependence
is a pre-item performing the object depended.command
is the specific command under the performing object.Examples: https://github.com/carloscn/clab/blob/master/macos/test_makefile/single/Makefile.hello
1.2 common options
By default, make command search
GUNmakefile
makefiles
Makefile
in current directory using as the make input file, except for that use option-f
to specify a make file by filename.-f
: specify a make file by file path-v
: show the version number-n
: perform the makefile, output commands record only, don't perform.-s
: perform the makefile, don't show commands record.-w
: show paths before and after execution.-C
: show paths where the makefile is located.1.3 using a common include make
We can use a makefile as a public makefile liking as the function
#include
in C language.Note, the
=
and:=
:=
: the final value assignment, whatever the calling before the assigning value or behind the assigning value.:=
There is a example for describing the difference between
=
and:=
:The
make a
output is456 456 789
, and themake b
output is an error, becasue the object b cannot ensure the y value that referenced itself.Now, we replaced the
=
to:=
:The
make a
output is456 123 789
, and themake b
is no longer an error, rather then789 789
.1.4 using the linux shell within makefile
https://github.com/carloscn/clab/blob/master/macos/test_makefile/Makefile.shll
1.5 using the nesting function of makefile
https://github.com/carloscn/clab/blob/master/macos/test_makefile/multi/src/Makefile
1.6 using condition define
https://github.com/carloscn/clab/blob/master/macos/test_makefile/Makefile
This is always combined
gcc -D
withifdef
. Sometimes, we want to use some variables of the makefile in a C source file, different variables different flows, such as using the debug, and setting the version number. Using thegcc -D
is equal to defining a macro by#define
.And the test.c is :
So, we can utilize the
ifdef
of makefile and-D
of gcc to do something.1.7 using the foreach
In a makefile, the foreach can provide the loop function for us. We can make use of the foreach to create a directory or something.
https://github.com/carloscn/clab/blob/master/macos/test_makefile/Makefile
1.8 using the function
https://github.com/carloscn/clab/blob/master/macos/test_makefile/Makefile
2. make val
We need to set up a C build environment shown in the following picture, and you can find the original source code in https://github.com/carloscn/clab/tree/master/macos/test_makefile/single
The main.c file depends on add.c div.c mul.c sub.c, and the main.c is implement simple add or div functions by calling the sub-functions.
We written the makefile as follow:
It has a very basic makefile and appears quite silly. All the objects would be recompiled when we altered a single file. If the project is particularly large, compiling will take a lot of your time.
So we need to optimize the makefile and make it easier and more convenient.
When one of these files changed, the optimized makefile would re-compile the changed .c file.
2.1 using user env
Based on the simple makefile, we replaced
*.o
files withOBJ
by user environment. The difference between the simple makefile and the new makefile is shown in the following figure:2.2 using sys env
$*
: the object name that does not include the extension name.$+
: all the dependence files, seperated by space.$<
: the first condition in rules.$?
: the dependence files that the time later than object.$^
: all the dependence files are not repeated.$%
: if the object is an archive member, the variable indicates the member name of the object.Based on the user env makefile, we replaced
OBJ
andTARGET
with$^
and^@
by the system environment.The difference between these makefiles is shown in the following figure:
2.3 using build env
The build env can be shown by
make -p
.AS
: asCC
: gccCPP
: ccCXX
: g++ or c++RM
: rm -rfBased on the system env makefile, we replaced
gcc
to$(CC)
by build environment.2.4 phony object
The phony object is that whether the file is changed or not, the object declared by phony must be performed.
If there is a hello file on your makefile path, and hello is still an object in your makefile. When the hello is not declared by phony:
If the hello is declared by .PHONY, the object will be performed by making command whether the file with the same name exists in the directory or not.
2.5 mode matching
%.*:%.c
: .o files depend on .c files corresponding.wildcard
:$(wildcard ./*.c)
obtain the all .c file in the current directory.patsubst
:$(patsubst %.c, %.o, $(wildcard *.c))
replace .c to .o corresponding.using the wildcard
3. Using libraries
3.1 Using the dynamic library
We need to review that how to comiple and use dynamic library using the
gcc
. Please refer to section 1 of 05_ELF文件_动态链接.When we generate a dynamic library on Linux:
-fPIC
: this is the position-independent code-shared
: this is specifying the type of dynamic library.When we use a dynamic library on Linux:
-l
: this is specifying the dynamic library name-L
: this is specifying the path of the dynamic library.-I
: this is specifying the path of included files.Now, we create files shown in the following figure:
cacu.c:
cacu.h:
Makefile:
3.2 Using the static library
We can make a static library compiling within a makefile. Compared with a dynamic library, the static library needs not original library files during the application running, in other words, the static library has been inserted into the object file, which leads to the application being bigger than the one compiled by the dynamic library. For a static library generation, we can refer to section 3 of # 03_ELF文件_静态链接. The common commands summary is as follows:
As we compile a static library:
gcc -c hello.c -o hello.o
ar -r libhello.a hello.o
As we use a static library:
gcc -lhello -L./ main.c -o main.elf
Example:
Touch a
mod.c
Touch a
mod.h
The makefile is:
4. Make Note
There are some notes of the make tool.
4.1 ignore an error
Prevent the make command from stopping when the script runs into an error. If the make command captured the non-zero return value, the make script would halt and return this error. However, some errors in the process returned are meaningless. We can enable the function of make ignoring errors in your makefile script.
make -i
: ignore all errors during the making..IGNORE
-
before one command line.4. Ref
[^1]: make.html [^2]: makefile从入门到项目编译实战