LinuxC:Makefile的编写和⽰例
make⼯具是Unix/Linux 的⼀个编译⼯具,它按照顺序读取 Makefile 或 makefile ,进⾏⾃动地有选择地执⾏编译链接,只对影响到的修改的⽂件进⾏重新编译,不需要对整个⼯程进⾏重新编译。⽽Makefile中些内容的就是它的编译⽅式。
Makefile 的格式:
⽬标项依赖项列表
[target] : file1 file2 file3 ...
规则
<tab>command1
<tab>command2
......
target是⼀个⽬标⽂件,也可以是Object File,也可以是执⾏⽂件。还可以是⼀个伪⽬标。这是⼀个⽂件
的依赖关系,target这⼀个或多个的⽬标⽂件依赖于 依赖项列表中的⽂件,其⽣成规则定义在command中。说⽩⼀点就是说,依赖项列表中如果有⼀个以上的⽂件⽐target⽂件要新或者target⽂件不存在的话,command所定义的命令就会被执⾏,如果依赖不存在或者更旧,那么就会执⾏⽣成依赖的对应target。如果依赖列表中的伪⽬标总是会被执⾏,相对于⼦程序段⼀样,先从左开始判断依赖列表,如果依赖时⽂件则判断是否要更新,如果是伪⽬标则直接执⾏。伪⽬标所依赖的伪⽬标也会被跟着执⾏。为了避免伪⽬标和⽂件名冲突,可以显⽰指定伪⽬标,关键字
为.PHONY:
依赖项就是,要⽣成那个target所需要的⽂件或是⽬标。
command也就是make需要执⾏的命令,通常是shell命令
例如
在⼀个⽂件 mysum.c中写⼊如下代码
在主程序 helloword.c 写⼊如下代码 ,可以看出 主程序中引⽤到了 mysum.c
如果要编译 以上两个⽂件,那么要在Makefile中写上
output: helloworld.c mysum.c #格式⽬标头: 依赖⽂件列表
gcc -o output helloworld.c mysum.c #格式每⾏开始必须是tab制表符
执⾏ make 或者 make -f Makefile 进⾏编译 。 其中 -f 指定⼀个编译过程的⽂件名(默认是Makefile,编译命令格式 make [⽬标名,默认值为第⼀个伪⽬标] -f [makefile⽂件名]。当连续执⾏第⼆次时,make就不会再构建没被更改的⽬标。
Makefile的编写也可以作些改进,例如把⼀些关键命令定义成变量,写在⽂件头,例如gcc ,假如之后要改⽤g++ 来编译呢,难不成要在全⽂⼀个个出gcc再替换成g++?其实把关键命令定义成变量就可以应对编译⽅式的变更操作了。如果编译的⽂件太多达到⼏百个怎么办?可以⽤include 关键字来引⽤其他⽂件内容,再其他⽂件中⽤变量来接所有要编译的⽂件名。由于命令执⾏失败会⾃动中断make,可以再命令前加 ‘-’ 符号,表⽰此命令如果执⾏失败则⽆视报错继续执⾏往下的命令。
如果在makefile中匹配/查询⽂件,如果在当前⽬录未到,可在(⼤写)VPATH和其⼦⽬录下继续。 关于(⼩写)vpath语
法, vpath [过滤条件] [查询⽬录] , 例如 vpath %.c /home/hu 表⽰ 在makefile中匹配/查询⽂件,如果在当前⽬录未到则可以在/home/hu/⽬录和⼦⽬录下的所有 .c的后缀的⽂件名列表继续。
Makefile 的⾃动变量
$@当前⽬标名
$<;第⼀个依赖项名
$^所有依赖项名
$*不包含扩展名的当前依赖项名
$?⽐当前⽬标新的依赖项列表
$#依赖项个数
C++项⽬型Makefile⽰例
#变量定义
LIB_NAME = libQuantLib.so
-include ql_var.mk #这⾥⾯包含QL_SRC_DIR ,HEADER_DIR 等变量信息
CXX = g++
DEFIND = -DQL_ERROR_FUNCTIONS
DEFIND += -DQL_ERROR_LINES
CXXFLAGS = $(DEFINE) $(addprefix -I, $(HEADER_DIR)) -O2 -Wall -c -fmessage-length=0 -fpic -fno-strict-aliasing
shell创建文件并写入内容HEADER_DIR = "."
HEADER_DIR += "$(BOOST_INCLUDE)"
HEADER_DIR += "$(ZLOG_INCLUDE)"
HEADER_DIR += "$(JAVA_HOME)/include"
HEADER_DIR += "$(JAVA_HOME)/include/linux"
SRC_DIR = $(QL_SRC_DIR) #源⽂件集合变量 ,定义在ql_var.mk
DEP_DIR = $(QL_DEP) #依赖集合变量 ,定义在ql_var.mk
vpath %.hpp $(HEADER_DIR)
vpath %.cpp $(SRC_DIR)
all: build_dir ql #执⾏make或者make all 相当于执⾏ make build_dir 和make ql
$(BUILD_DIR)/%.o : %.cpp #⽣成在build⽬录下的 .o⽂件的⽣成规则
$(CXX) $(CXXFLAGS) -MMD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<"
-include $(DEP_FILE)
.PHONY: build_dir
build_dir:
@mkdir -p $(BUILD_DIR)
.PHONY: scan #执⾏ make scan 相当于先执⾏ make build_dir 再执⾏⾃⾝命令
scan: build_dir
@echo Scanning Project ....
@./scan.sh #执⾏shell,此shell的⽬的是创建ql_var.mk,并在此⽂件中定义要编译的⽂件和其依赖
ql: $(LIB_OBJ) $(QL_OBJ) # $(LIB_OBJ) $(QL_OBJ)为.o⽂件名集合,在ql_var.mk中被定义,如果发现依赖被更新则⾃动触发 .o⽂件的⽣成规则 $(CXX) -share -o $(LIB_NAME) $^ "$(ZLOG_LIB)/libzlog.a"
.PHONY: clean
clean:
@echo Clean Project
-
@rm -rf $(BUILD_DIR)
-@rm -f ql_var.mk
执⾏编译顺序
make clean #清空编译⽬录和删除变量⽂件
make scan #创建编译⽬录和执⾏scan.sh ,从⽽创建变量⽂件
make #执⾏make all,创建编译⽬录和链接⽬标⽂件(.o),需要链接的⽂件列表来源于变量⽂件中,如果依赖项缺失,则编译出依赖⽂件
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论