一、说明
使用linux gcc编译程序时,需要输入很多命令及参数,很复杂,容易出错,并且会浪费很多时间。Makefile应运而生,使用Makefile来管理整个软件工程的编译流程,在实际软件工程中,通过make一条指令就可以完成整个软件工程的编译。
作用:
- 大量代码的关系维护
- 减少重复编译时间
二、Makefile构成
Makefile三要素:
目标
依赖
执行命令,命令要以table开头
多条命令,每条占一行
执行方式:
make
make -f my-makefile
三、常见命令
- make
- 查看版本
$ make -v
GNU Make 3.81
Copyright © 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for x86_64-pc-linux-gnu
- 打印命令,但不执行
用于调试
$ make -n
echo “compile start…”
gcc main.c
四、符号说明
< :第一个依赖文件
@ :目标文件的名称
^ :依赖文件(不重复)
- :目标文件的名称,不包含文件的扩展名
? :修改过的依赖文件
- :所有的依赖文件(包含重复)
五、 Makefile中的函数
调用方法:
$(,)
${,}
• function是函数名,arguments是函数的参数。 参数之间要用逗号分开。
• 可以使用小括弧,或者花括弧,一般选择小括弧。
-
获取匹配模式文件名:wildcard函数
wildcard – 查找指定目录下的指定类型的文件
src = $(wildcard *.c) //找到当前目录下所有后缀为.c的文件,赋值给src -
模式字符串替换:patsubst函数
obj = $(patsubst %.c,%.o, $(src)) //把src变量里所有后缀为.c的文件替换成.o -
字符串替换:subst函数
-
notdir 函数
-
Suffix
-
过滤函数:filter
makefile中也提供了一些变量(变量名大写)供用户直接使用。
CC = gcc #arm-linux-gcc
CPPFLAGS : C预处理的选项 如:-I
CFLAGS: C编译器的选项 -Wall -g -c
LDFLAGS : 链接器选项 -L -l
-
宏定义使用
A:宏定义
-D DEBUG
-D DEBUG=[value] -
ifeq的使用
ifeq ( ( T A R G E T A R C H ) , a r m ) L O C A L S R C F I L E S : = . . . e l s e i f e q ( (TARGET_ARCH), arm) LOCAL_SRC_FILES := ... else ifeq ( (TARGETARCH),arm)LOCALSRCFILES:=...elseifeq((TARGET_ARCH), x86)
LOCAL_SRC_FILES := …
else ifeq ($(TARGET_ARCH), mips)
LOCAL_SRC_FILES := …
else
LOCAL_SRC_FILES := …
endif -
注释方法
使用#作为注释方法 -
忽略错误
Makefile:
all:
gcc main.c
.PHONY:
clean:
-rm a.out
$ make clean
rm a.out
rm: cannot remove ‘a.out’: No such file or directory
make: [clean] Error 1 (ignored)
$ make clean
rm a.out
rm: cannot remove ‘a.out’: No such file or directory
make: [clean] Error 1 (ignored)
- 不打印命令
$ make
gcc main.c
Makefile:
all:
@gcc main.c
.PHONY:
clean:
-@rm a.out
$ make
$ make clean
- 日志打印
Makefile:
all:
@echo "compile start..."
@gcc main.c
.PHONY:
clean:
-@rm a.out
$ make
compile start...
六、 实例说明
1.条件编译的使用
2.1 使用静态库
LIB_A_SUPPORT=y
#LIB_SO_SUPPORT=y
编译:
$ make
echo ./lib/src/math_method.c math_method.o
./lib/src/math_method.c math_method.o
gcc -fPIC -c ./lib/src/math_method.c
ar cr libmath_method.a math_method.o
ranlib libmath_method.a
gcc -o a.out main.c -L. -Wl,-dn -lmath_method -Wl,-dy -lgcc_s -I./lib/include
测试:
$ ./a.out
hello world
1 + 9 = 10
2.2 使用动态库
LIB_SO_SUPPORT=y
#LIB_A_SUPPORT=y
编译:
$ make
echo ./lib/src/math_method.c math_method.o
./lib/src/math_method.c math_method.o
gcc -fPIC -c ./lib/src/math_method.c
gcc -shared -o libmath_method.so math_method.o
gcc -o a.out main.c -L. -Wl,-dy -lmath_method -Wl,-dy -lgcc_s -I./lib/include
测试:
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/001-program
$ ./a.out
hello world
1 + 9 = 10
源码:
$ tree .
.
├── lib
│ ├── include
│ │ └── math_method.h
│ └── src
│ └── math_method.c
├── main.c
└── Makefile
main.c:
#include <stdio.h>
#include "math_method.h"
int main(int arc, char *argv[])
{
printf("hello world\n");
printf("%d + %d = %d\n", 1, 9, math_add(1, 9));
return 0;
}
math_method.c:
#include <stdio.h>
int math_add(int a, int b)
{
return a + b;
}
int math_sub(int a, int b)
{
return a - b;
}
math_method.h:
#ifndef __MATH_METHOD_H__
#define __MATH_METHOD_H__
int math_add(int a, int b);
int math_sub(int a, int b);
#endif
Makefile:
LIB_A_SUPPORT=y
LIB_SO_SUPPORT=y
CC=gcc
ifeq ($(LIB_A_SUPPORT), y)
LIBRARY=-L. -Wl,-dn -lmath_method -Wl,-dy -lgcc_s
endif
ifeq ($(LIB_SO_SUPPORT), y)
LIBRARY=-L. -Wl,-dy -lmath_method -Wl,-dy -lgcc_s
endif
INCLUDE=-I./lib/include
CFLAGS=
TARGET=a.out
ifeq ($(LIB_SO_SUPPORT), y)
DEPEND_TARGET += libmath_method.so
endif
ifeq ($(LIB_A_SUPPORT), y)
DEPEND_TARGET += libmath_method.a
endif
LIB_SRCS=$(wildcard ./lib/src/*.c)
LIB_NDIR_SRCS=$(notdir $(LIB_SRCS))
LIB_OBJ=$(patsubst %.c, %.o, $(LIB_NDIR_SRCS))
$(TARGET): $(DEPEND_TARGET)
gcc -o $@ main.c $(LIBRARY) $(INCLUDE) $(CFLAGS)
$(LIB_OBJ): $(LIB_SRCS)
echo $(LIB_SRCS) $(LIB_OBJ)
$(CC) -fPIC -c $(LIB_SRCS)
ifeq ($(LIB_A_SUPPORT), y)
libmath_method.a: $(LIB_OBJ)
ar cr $@ $^
ranlib $@
endif
ifeq ($(LIB_SO_SUPPORT), y)
libmath_method.so: $(LIB_OBJ)
$(CC) -shared -o $@ $^
endif
.PHONY:
clean:
-@rm -rf *.o *.a *.so
-@rm $(TARGET)
伪目标的使用
Makefile
a.out: hello_world_main.c
gcc -o a.out hello_world_main.c
clean:
-rm a.out
在上面的Makefile中,a.out是默认目标,clean也是一个目标。
执行make
$ make
gcc -o a.out hello_world_main.c
执行make clean
$ make clean
rm a.out
如果Makefile所在目录下有目标clean同名的文件时,执行make clean会一直提示”make: `clean’ is up to date.”
$ make clean
make: `clean' is up to date.
因为clean目标只是为了执行一些清理命令,而不是真正的目标。为了解决上述问题,可以将clean定义为伪目标,如下所示:
Makefile:
a.out: hello_world_main.c
gcc -o a.out hello_world_main.c
.PHONY:clean
clean:
-rm a.out
再执行make clean时,不会提示”`clean’ is up to date.”,如下:
$ make clean
rm a.out
rm: cannot remove ‘a.out’: No such file or directory
make: [clean] Error 1 (ignored)
二、内置函数的使用
- wildcard
$ tree .
.
├── log.h
├── log_int.c
├── log_str.c
├── main.c
└── Makefile
log_int.c:
#include <stdio.h>
int show_int(int value)
{
printf("value: %d\n", value);:
return 0;
}
log_str.c:
#include <stdio.h>
int show_log(char *str)
{
printf("str: %s\n", str);
return 0;
}
log.h:
#ifndef __LOG_H__
#define __LOG_H__
int show_log(char *str);
int show_int(int value);
#endif
main.c:
#include <stdio.h>
#include "log.h"
int main(int arc, char *argv[])
{
show_log("hello world");
show_int(5);
return 0;
}
Makefile:
SRC=$(wildcard *.c)
a.out: $(SRC)
gcc -o a.out $(SRC)
.PHONY:clean
clean:
-rm a.out
编译:
$ make
gcc -o a.out log_int.c log_str.c main.c
Makefile:
file_name=$(notdir /tmp/a.c,/tmp/b.c)
filter_list=$(filter %.c %.h, a.c b.c m.h m.o)
suffix_name=$(suffix test.c)
new_obj = $(patsubst %.c, %.o, a.c b.c)
obj=$(subst .c,.o,"a.c b.c")
all:
@echo "file_name: "$(file_name)
@echo "filter_list: "$(filter_list)
@echo "suffix_name: "$(suffix_name)
@echo "new_obj: "$(new_obj)
@echo "obj: "$(obj)
测试:
$ make
file_name: b.c
filter_list: a.c b.c m.h
suffix_name: .c
new_obj: a.o b.o
obj: a.o b.o
七、基础实例
源码工程
目录结构:
.
├── hello_world_main.c
└── Makefile
hello_world_main.c:
#include <stdio.h>
int main(int arc, char *argv[])
{
printf("hello world\n");
return 0;
}
a.out: hello_world_main.c
@gcc hello_world_main.c
.PHONY:
clean:
-@rm a.out
a. out和clean表示目标, hello_world_main.c是a.out依赖的文件
目标下面的内容是执行的指令,如 “gcc -o a.out hello_world_main.c”和“-@rm a.out”,指令以table头,可以有多行
#开头的行是注释
第一个目标是默认目标,如a.out
默认目标直接使用make即可完成编译,其它目标需要显示指明,比如clean目标,需要使用make clean
Makefile文件名不区分大小写,一般使用首字母大写的形式
Makefile可以管理复杂的编译过程,如多种条件、多个目录及文件等
clean用途: 清除编译生成的中间.o文件和最终目标文件
make clean 如果当前目录下有同名clean文件,则不执行clean对应的命令,解决方案:
伪目标声明: .PHONY:clean
编译及运行
编译:
$ make
gcc -o a.out hello_world_main.c
运行:
$ ./a.out
hello world
删除编译文件:
$ make clean
依赖文件没有更新时,重复执行make不会重新编译
$ make
make: `a.out' is up to date.
修改依赖文件hello_world_main.c
hello_world_main.c
#include <stdio.h>
int main(int arc, char *argv[])
{
printf("hello world\n");
printf("how are you\n");
return 0;
}
$ make
$ ./a.out
hello world
how are you