1 makefile例子
写这个例子的目的,由于工作中多数情况下是维护模块,makefile文件已经写好了,后面维护只是添加或修改文件,
所以很少写这玩意,这次负责一个新模块,需要自己写这个模块的makefile,突然发现之前的makefile都忘差不多了,
因此写个小例子实验并记录一下,方便以后参考,省的到处查找。
例子中程序内容不重要,重要的是makefile文件,这个例子目的是生成libcacu.so库,生成的库和中间文件.o放
在指定的目录下。
1.1 目录结构
cacu/
|-- add
| |-- add.c
|-- dbg
| |-- dbg.c
|-- div
| |-- div.c
|-- mul
| |-- mul.c
|-- sub
|-- sub.c
|-- inc
| |-- add.h
| |-- cacu.h
| |-- comm.h
| |-- div.h
| |-- mul.h
| |-- sub.h
| |--dbg.h
|-- Makefile
1.2 makefile文件
SRC_DIR = add sub mul div dbg
#VPATH = add:sub:mul:div:dbg
vpath %.c add:sub:mul:div:dbg #查找依赖时如果遇到%.c,则自动到add:sub:mul:div:dbg目录下寻找
CUR_DIR = $(shell pwd)
SRC = $(foreach dir,$(SRC_DIR),$(wildcard $(dir)/*.c))
$(info "SRC:$(SRC)")
OBJ = $(patsubst %.c,%.o,$(SRC))
$(info "OBJ:$(OBJ)")
TARGET_NAME = libcacu.so
TARGET_TYPE = so# "#":标识结束,纺织在ifeq时出现预想不到的结果 ,so:dynamic library,bin:exe,a:static library
CFLAGS = -fPIC -shared
INCLUDE = -I./inc
CC = gcc
#指定在源码目录意外的目录即可,原因,编译过程不影响源码的目录结构,方便提交代码,这里指定和源码同级的out目录
OUT_DIR = $(CUR_DIR)/../out
$(info "OUT_DIR:$(OUT_DIR)")
OBJ_DIR = $(OUT_DIR)/obj
$(info "OBJ_DIR:$(OBJ_DIR)")
BIN_DIR = $(OUT_DIR)/bin
$(info "BIN_DIR:$(BIN_DIR)")
LIB_DIR = $(OUT_DIR)/lib
$(info "LIB_DIR:$(LIB_DIR)")
OBJ_WITHOUT_DIR = $(notdir $(OBJ))
$(info "OBJ_WITHOUT_DIR:$(OBJ_WITHOUT_DIR)")
OBJ_WITH_DIR = $(addprefix $(OBJ_DIR)/,$(OBJ_WITHOUT_DIR))
$(info "OBJ_WITH_DIR:$(OBJ_WITH_DIR)")
ifeq ($(TARGET_TYPE),so)# 逗号和so之间最好有一个空格,可以没有,so后切记不要有空格
TARGET = $(LIB_DIR)/$(TARGET_NAME)#切记不要用tab键空格,想要空格的话可以space键打出空格,再强调一边不要用tab键!!!!!!!!!!
$(info "TARGET:$(TARGET)")
endif
all: dir_create $(TARGET)
FORCE: #force destion 没有使用,没删出的目的,记录下强制执行的方式
$(TARGET):$(OBJ_WITH_DIR)
ifeq ($(TARGET_TYPE),so)# 逗号和so之间最好有一个空格,so后切记不要有空格
$(CC) $(CFLAGS) $(INCLUDE) $^ -o $@
endif
#定义创建目录的功能块(由于类似与函数,一般叫做函数)
define CRT_DIR
if [ ! -d $(1) ];\
then\
mkdir -p $(1);\
echo "******$(1) created success!!!******";\
else\
echo "******$(1) has been created!!!******";\
fi
endef
#这个目标用于创建目录,并且是总目标依赖的第一个目标,这样执行总目标时可以保证第一个执行,因为后面的目标依赖这个这个目标创建的目录
dir_create:
@$(call CRT_DIR,$(OUT_DIR))
@$(call CRT_DIR,$(OBJ_DIR))
@$(call CRT_DIR,$(BIN_DIR))
@$(call CRT_DIR,$(LIB_DIR))
#目录结构的规则模式替换,因为.o文件在OBJ_DIR目录下,所以要加OBJ_DIR路径
#OBJ_DIR = ../out/obj 也就是所有的中间文件.o都在这个目录下面。
#这时很容易联想到那么.c文件也应该加上目录SRC_DIR ,是的,一开始我也是这样做的,但结果是不行的
#原因,我的猜测是SRC_DIR是多个目录导致的,不能这样用,具体原因不清楚。
#到网上搜相关的内容,找到都是单个目录的(SRC_DIR只有一个值)
#如果是多个目录必须使用vpath或VPATH(个人看法)
#所以添加了一句 vpath %.c add:sub:mul:div:dbg ,意思是查找依赖时如果遇到%.c,则自动到add:sub:mul:div:dbg目录下寻找
#目录结构的规则模式替换,因为.o文件在OBJ_DIR目录下,所以要加OBJ_DIR路径
#源文件.c并不在同一个目录下,所以不能直接将SRC_DIR加到%.c前,需要使用VPATH或vpath自动化变量辅助
$(OBJ_DIR)/%.o:%.c
$(CC) -c $< -o $@
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR) $(LIB_DIR)
1.3输出目录
cacu/
out/
|-- bin
|-- lib
| |-- libcacu.so
|-- obj
|-- add.o
|-- dbg.o
|-- div.o
|-- mul.o
|-- sub.o
说明:out目录和cacu在同一级(源码以外的目录),这样做的原因,编译的过程不改变代码目录结构,方便代码的提交
1.4 makefile 分析
1.4.1 makefile解释
1 查找依赖时如果遇到%.c,则自动到add:sub:mul:div:dbg目录下寻找
vpath %.c add:sub:mul:div:dbg
2 遍历$(SRC_DIR)的所有子目录同时把子目录下的.c文件遍历出来
SRC = $(foreach dir,$(SRC_DIR),$(wildcard $(dir)/*.c))
SRC = add/add.c sub/sub.c mul/mul.c div/div.c dbg/dbg.c
3 把.c替换成.o
OBJ = $(patsubst %.c,%.o,$(SRC))
OBJ = :add/add.o sub/sub.o mul/mul.o div/div.o dbg/dbg.o
4 获取不含路径的.o
OBJ_WITHOUT_DIR = $(notdir $(OBJ))
OBJ_WITHOUT_DIR = add.o sub.o mul.o div.o dbg.o
5 增加前缀$(OBJ_DIR)/
OBJ_WITH_DIR = $(addprefix $(OBJ_DIR)/,$(OBJ_WITHOUT_DIR))
OBJ_WITH_DIR = /home/workspace/cacu/../out/obj/add.o /home/workspace/cacu/../out/obj/sub.o /home/workspace/cacu/../out/obj/mul.o /home/workspace/cacu/../out/obj/div.o /home/workspace/cacu/../out/obj/dbg.o
6 调用创建目录的函数
$(call CRT_DIR,$(OUT_DIR))
创建OUT_DIR目录
7 打印变量的值方便调试
$(info "OBJ:$(OBJ)")
1.4.2 makefile测试文件(有时间再上传)
可以根据描述的目录结构自己完成,程序内容就是加、减、乘、除,可以忽略dbg文件。