makefile学习


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也是类似)

  1.    读入所有的Makefile。
    
  2.    读入被include的其它Makefile。
    
  3.    初始化文件中的变量。
    
  4.    推导隐晦规则,并分析所有规则。
    
  5.    为所有的目标文件创建依赖关系链。
    
  6.    根据依赖关系,决定哪些目标要重新生成。
    
  7.    执行生成命令。
    

规则包含两个部分,一个是依赖关系,一个是生成目标的方法。

make一般是使用环境变量SHELL中所定义的系统Shell来执行命令,默认情况下使用UNIX的标准Shell——/bin/sh来执行命令。

变量

在 Makefile中的定义的变量,就像是C/C++语言中的宏一样,他代表了一个文本字串,在Makefile中执行的时候其会自动原模原样地展开在所使用的地方。其与C/C++所不同的是,你可以在Makefile中改变其值。在Makefile中,变量可以使用在“目标”,“依赖目标”,“命令”或是 Makefile的其它部分中。

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“$”字符,那么你需要用“$$”来表示。变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。

$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。

= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值

模式规则

%.o : %.c ; 

其含义是,指出了怎么从所有的[.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"

你可能感兴趣的:(makefile学习)