Makefile的编写总结

在Linux做开发,工程的编译输出,需要自己编写Makefile来构建,下面笔者根据自己的实践,做一些总结,本文并不是什么教程,

只是笔者觉得一些重要的东西在此记录下来。



一、基本格式
target : depend
command
target 是要生成的目标文件,可以是可执行文件、*.o、*.a、*.so等文件,当然也可以一个伪目标。
depend 是依赖文件,可以是源文件,头文件,*.o文件,当然也可以是一个伪依赖。
command 是执行命令,即如何编译依赖文件生成目标文件。值得注意的是command前面必须是一个tab键。

eg : main.c depend.c depend.h  生成可执行文件main
main:main.o depend.o
        gcc main.o depend.o -o main
main.o : main.c
        gcc -c -Wall main.c
depend.o:depend.c depend.h
       gcc -c -Wall depend.c

二、变量
Makefile中可以自定义变量,有的地方也称之为宏,变量名尽量用数字、字母和下划线命名,并且对大小写敏感。
    变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表等。
    eg: CC = gcc
    CFLAG = -c -Wall
    引用一个变量是这样:$(VarName),例如:$(CC) , $(CFLAG)
    笔者的使用习惯,定义变量全都是大写,显然这和C语言的宏定义一样,称之为宏也不为怪。
    这里的赋值号前后可以有空格,这与Shell脚本不同,Shell中定义变量,赋值号前后不能有空格。
    改写上面的Makefile:
    CC = gcc
    CFLAG = -c -Wall
    TARGET = main
    OBJ = main.o depend.o
    $(TARGET):$(OBJ)
             $(CC) $(CFLAG) $(OBJ) -o $(TARGET)
    main.o : main.c
            $(CC) $(CFLAG) main.c
    depend.o:depend.c depend.h
            $(CC) $(CFLAG) depend.c

三、特殊变量
    $@ 表示目标对象
    $^ 表示所有依赖对象
    $< 表示第一个依赖对象
    VPATH 指定源文件的搜索路径,它和linux下环境变量类似 
    vpath 和VAPTH类似,但是比VPATH更灵活
    继续改写上面的Makefile:
    CC = gcc
    CFLAG = -c -Wall
    TARGET = main
    OBJ = main.o depend.o
    $(TARGET):$(OBJ)
              $(CC) $(CFLAG) $^ -o $@
    main.o : main.c
              $(CC) $(CFLAG) $<
    depend.o:depend.c depend.h
              $(CC) $(CFLAG) $<

四、项目案例
Project
├── Bin
├── Build
├── Lib        --liblog.a libsqlite.a
└── Source
   ├── Src1   --Demo1.c Demo1.h
   ├── Src2   --Demo2.c Demo2.h
   └── Src3   --Demo3.c Demo3.h
如上图所示,工程项目Project下有四个目录--Bin,Buld,Lib,Source
Source是源代码目录,其下又有三个子目录Src1,Src2,Src3,在实际项目中,源文件一般按功能模块放在不同的目录下,便于管理。
Lib目录是库目录,根据笔者的经验,这样的目录放第三方的库,如果是开源的库,源代码可以放入其中,编译成一个库文件,链接程序时用到。因为第三方的开源库很少需要修改,编译成库文件后,就可以一直使用了,所以要把他们单独放在Lib文件目录里。
Build目录是编译构建目录,Makefile放在里面,编译时生成的中间文件*.gch,*.o都会在这里,不会散落在源代码目录里,这个目录很有必要。
Bin是生成的二进制程序目录,当然这个目录其实软件运行环境目录,这个目录是要部署到机器上的,一个可执行程序的运行,其实是有很多依赖的,比如可能要读取xml配置文件,可能要依赖某个文件创建进程通信的Key值,在这里我简化了它,Bin目录下没有其他子目录,只存放可执行文件。
 
假设本工程最终生成可执行文件main,依赖Source下的所有源文件,并且依赖Lib下的liblog.a libsqlite.a
  Makefile如下,文件名Makefile1
  CC = gcc
  CFLAG = -c -Wall
  BIN = ../Bin
LIB = ../Lib
SOURCE = ../Source
LINKLIB = -L$(LIB) -lmylog -lsqlite
VPATH = $(SOURCE)/Src1:$(SOURCE)/Src2:$(SOURCE)/Src3
TARGET = main
OBJ = Demo1.o \
            Demo2.o \
            Demo3.o
      
all:$(TARGET)
       cp $(TARGET) $(BIN) 
$(TARGET):$(OBJ)
        $(CC) $(CFLAG) $^ $(LINKLIB) -o $@
%.o:%.c
        $(CC) $(CFLAG) $<
 
clean:
        rm *.o main
 
 说明:1、 Makefile文件是在Build目录中,因此Makefile的工作路径就是这个路径,定义其他路径时都是相对于Makefile的工作路径,
  当然执行Makefile时也必须在这个路径
2、开头定义了几个变量,这样做的好处是使Makefile更容易修改维护
3、VPATH定义了源文件寻找目录,以冒号间隔,Make会自动到这几个目录寻找源文件
4、定义OBJ用的字符‘\’表示行连接符
5、all 是一个伪目标,执行命令仅仅是把生成的可执行程序main拷贝到Bin目录,和下面的clean类似
6、%.o:%.c 是Make的一种自动推导方式,表示一个.o文件会依赖一个对应的.c文件,本例中Make会自动到当前目录或者VPATH中寻找依赖Demo1.c Demo2.c Demo3.c

五、常用函数
其实Makefile里也有函数的,有函数定义和调用的语法格式,但是笔者只使用过内置函数,没有使用过自定义函数。
下面仍然用项目案例来介绍说明几个常用的内置函数
1、wildcard 
这个词就是通配符的意思,是通配符函数,通配符在正则匹配中经常使用,并不陌生。
这个函数的功能是按照匹配模式,生一个以空格间隔的列表,例如 SRC = $(wildcard *.c ./foo/*.c) 表示把当前目录下的.c文件和当前目录子目录foo的.c文件,以列表的形式赋值给SRC
语法格式 $(wildcard pattern1 pattern2 ...) ,注意参数以空格隔开
2、patsubst
即pattern substitute ,模式取代,以特定模式替换字符串
语法格式:$(patsubst from,to, str)
SRC = Demo1.c Demo2.c Demo3.c
OBJ = $(patsubst %.c,%.o,$(SRC)) //把.c替换成对应的.o
此时OBJ展开就是: OBJ = Demo1.o Demo2.o Demo3.o 
 
3、subst
字符串替换,与patsubst不同,subst只是暴力的把一个字符串替换成另一个字符串
语法格式:$(subst from,to,str)
 
4、filter
过滤函数,保留符合模式的字符串
语法格式:$*(filter pattern1 pattern2 ...,text)
src = a.c b.c a.h c.sh d.o
csrc = $(filter %.c %.h,$(src)) //保留 .c .h文件
此时csrc展开 csrc = a.c b.c a.h 
 
5.filter-out
过滤函数,保留不符合模式的,与filter功能相反,语法也一样。
 
下面继续修改项目案例中的Makefile,文件名Makefile2
CC = gcc
CFLAG = -c -Wall
BIN = ../Bin
LIB = ../Lib
SOURCE = ../Source
LINKLIB = -L$(LIB) -lmylog -lsqlite
SRC = $(wildcard $(SOURCE)/Src1/*.c $(SOURCE)/Src2/*.c $(SOURCE)/Src3/*.c)
TARGET = main
OBJ = $(patsubst %.c,%.o,$(SRC))
      
all:$(TARGET)
      cp $(TARGET) $(BIN) 
$(TARGET):$(OBJ)
      $(CC) $(CFLAG) $^ $(LINKLIB) -o $@
%.o:%.c
      $(CC) $(CFLAG) $<
 
clean:
      rm *.o main
 
说明:这种使用函数的Makefile2显然比上面那个Makfile1更简洁,更有通用性,但是上面的Makfile2也有它的优势,
这个项目案例只是生成一个可执行程序,它依赖源目录下的所有源文件,当工程是生成多个可执行程序时,某个目标文件只会依赖

一些源文件,显然Makefile1中OBJ变量的定义更灵活,更清晰。


由于笔者的水平有限,出错在所难免,恳请读者拍砖指正,谢谢阅读


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值