title: 2017-6-15makefile
tags:makefile的书写和熟悉
示例
# 指令编译器和选项
CC=gcc
CFLAGS=-lssl -lcrypto -ldl -Wall -g -DDEBUG -std=gnu99
# 宏定义
DEFS = -DTEST_ADD -DTEST_SUB=1
CFLAGS += $(DEFS)
# 头文件查找路径
INC = -Iport -I../../modbus/rtu \
-I../../modbus/ascii -I../../modbus/include -I../../modbus/tcp
# 静态链接库
LDFLAGS =
LDLIBS = -lpthread
# 目标文件
TARGET=test
# 源文件
SRCS = test.c \
./test-add/test-add.c \
./test-sub/test-sub.c
# 头文件查找路径
INC = -I./test-add -I./test-sub
# 目标文件
OBJS = $(SRCS:.c=.o)
# 链接为可执行文件
$(TARGET):$(OBJS)
# @echo TARGET:$@
# @echo OBJECTS:$^
[tab]$(CC) -o $@ $^
clean:
[tab]rm -rf $(TARGET) $(OBJS)
# 连续动作,请清除再编译链接,最后执行
exec:clean $(TARGET)
[tab]@echo 开始执行
[tab]./$(TARGET)
[tab]@echo 执行结束
# 编译规则 $@代表目标文件 $< 代表第一个依赖文件
%.o:%.c
[tab]$(CC) $(CFLAGS) $(INC) -o $@ -c $<
# 指定编译器
CC = gcc
# CFLAG包括头文件目录
CFLAGS = -g -Wall
# 头文件查找路径
INC = -Iport -I../../modbus/rtu \
-I../../modbus/ascii -I../../modbus/include -I../../modbus/tcp
# 静态链接库
LDFLAGS =
LDLIBS = -lpthread
# 目标
TARGET = tcpmodbus
# 源文件
SRC = demo.c port/portother.c \
port/portevent.c port/porttcp.c \
../../modbus/mb.c ../../modbus/tcp/mbtcp.c \
../../modbus/functions/mbfunccoils.c \
../../modbus/functions/mbfuncdiag.c \
../../modbus/functions/mbfuncholding.c \
../../modbus/functions/mbfuncinput.c \
../../modbus/functions/mbfuncother.c \
../../modbus/functions/mbfuncdisc.c \
../../modbus/functions/mbutils.c
# 源文件编译为目标文件
OBJS = $(SRC:.c=.o)
# 链接为可执行文件
$(TARGET): $(OBJS)
[tab]$(CC) $^ -o $@ $(LDFLAGS) $(LDLIBS)
# 清除可执行文件和目标文件
clean:
[tab]rm -f $(OBJS)
[tab]rm -f $(TARGET)
# 编译规则 加入头文件 $@代表目标文件 $< 代表第一个依赖文件
%.o:%.c
[tab]$(CC) $(CFLAGS) $(INC) -o $@ -c $<
主要目的
是为了把一些可以变化的参数,编译到文件里面。
所以要关注#define 的作用域。
不管是typedef还是define,其作用域都不会扩展到别的文件,即使是同一个程序的不同文件,也不能互相使用。
语法概要
target... : prerequisites ...
command
【target】 也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)。
【prerequisites】 就是,要生成那个target所需要的文件或是目标。
【command】 也就是make需要执行的命令。(任意的Shell命令)
这个是一个大概的格式,后边所有的语法和变量都是为了简化这种写法。
【:】冒号是表示的依赖关系。依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。
make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。也就是【target】。
在Makefile中的命令,必须要以[Tab]键开始。
Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。
显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”。
GNU的make工作时的执行步骤入下:(想来其它的make也是类似)
读入所有的Makefile。
读入被include的其它Makefile。
初始化文件中的变量。
推导隐晦规则,并分析所有规则。
为所有的目标文件创建依赖关系链。
根据依赖关系,决定哪些目标要重新生成。
执行生成命令。
规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
make一般是使用环境变量SHELL中所定义的系统Shell来执行命令,默认情况下使用UNIX的标准Shell——/bin/sh来执行命令。
变量
在 Makefile中的定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,在Makefile中执行的时候其会自动原模原样地展开在所使用的地方。其与C/C++所不同的是,你可以在Makefile中改变其值。在Makefile中,变量可以使用在“目标”,“依赖目标”,“命令”或是 Makefile的其它部分中。
变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。
$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
模式规则
%.o : %.c ; <command ......>
其含义是,指出了怎么从所有的[.c]文件生成相应的[.o]文件的规则。如果要生成的目标是"a.o b.o",那么"%c"就是"a.c b.c"
BUG记录
用makefile定义了宏-DKEY="hello world"
但是gcc这样宏会忽略双引号。如果当成左值,则会把这句话(just)当成了声明变量。
并且
宏中有#运算符时,参数不会被展开
为了能让参数被顺利展开所以就不可以使用"#"符号,最终的宏定义如下
#define HELLO hello world
#define _A(str) _TMP(str)
#define _TMP(str) #str
std::cout << _A(HELLO) << std::endl;
1.展开_A,由于没有"#"符号,HELLO被顺利展开,变为 _TMP(hello world)
2._TMP把参数hello world转换为字符串,也就是加上双引号,变为 "hello world"