make总结(三):高级

一、几点细节帮助理解Makefile

  1. makefile中的变量引用方法为$(VAR), 若为$VAR,则make只对$V进行替换。(我的系统上是这样。)

  2. 若要在makefile中写shell指令,引用shell中的变量时的格式是$$VAR。make将$$VAR做一次转椅,扔给shell时,shell看到的是$VAR,符合shell中引用变量的格式。

  3. make的规则中的命令若没有;结尾,则每条指令都是在一个新的shell中,若想多条命令在一个shell中执行,可以用;将他们连起来。(涉及到shell执行的方式)

  4. make中存在大量隐式规则,简化makefile的编写。隐式规则可以被覆盖。

  5. make按照从上到下从左到右读入makefile中的内容,但是在处理makefile中的语句是并不是这样的顺序。

  6. make处理makefile分为两个阶段,第一个阶段读入makefile,以及包含的其他makefile,分析变量名及变量值,隐式规则和显式规则

    构建所有目标关系树,以及他们的先决条件。

    第二阶段根据第一阶段建立的关系树分析哪些目标需要重新构建,执行哪些命令来构建。

  7. make处理makefile时存在立即展开(第一阶段就展开)和延迟展开(第二阶段展开)。

      变量赋值时,赋值号左边总是立即展开,但是右边的变量却不一定。

    所有条件语句都是立即展开,自动变量都是延迟展开(他们都是在规则的命令块中被设置),所以自动变量在条件语句中不能使用。如果一定要使用,则只能使用shell条件语法。

  8. 规则的展开策略:目标和依赖都是立即展开,命令只能延迟展开(自动变量在命令中)。

二、举例 

  项目huge的目录结构如图所示:

  1. build目录下的Makefile的作用:分别进入./code/huge/src和./code/foo/src并执行子目录里面的makefile。

  2. 将两个子目录的Makefile的公用部分抽离到build下的make.rule。并在子目录中包含make.rule简化子目录的Makefile编写,增加复用性。

 1 .PHONY: all clean
 2
 3 # get the root of the project, you can use func: abspath
 4 ROOT=$(realpath ..)
 5
 6 # list all directory we need to go to.
 7 DIRS = $(ROOT)/code/foo/src  8     $(ROOT)/code/bar/src  9     $(ROOT)/code/huge/src
10
11 RM=rm
12 RMFLAFGS=-rf
13 RMS=$(ROOT)/build/bins $(ROOT)/build/libs
14
15 all clean:
16     #tell shell that if any error occur, exit immediately. then make will stop.
17     @set -e; 18     for dir in $(DIRS); 19     do 20         cd $$dir && $(MAKE) ROOT=$(ROOT) -r [email protected]; 21     done
22     @set -e; 23     if [ "$(MAKECMDGOALS)" == "clean" ]; then $(RM) $(RMFLAFGS) $(RMS); fi
24     @echo ""
25     @echo ":-) Completed"
26     @echo ""

./build/Makefile

 1 .PHONY: all clean
 2
 3 MKDIR=mkdir
 4 RM=rm
 5 RMFLAGS=-rf
 6
 7 CC=gcc
 8 AR=ar
 9 ARFLAGS=crs
10
11 DIR_OBJS=objs
12 DIR_BINS=$(ROOT)/build/bins
13 DIR_DEPS=deps
14 DIR_LIBS=$(ROOT)/build/libs
15 DIRS=$(DIR_OBJS) $(DIR_BINS) $(DIR_DEPS) $(DIR_LIBS)
16 RMS=$(DIR_OBJS) $(DIR_DEPS)
17
18 ifneq ("$(BIN)", "")
19 BIN:=$(addprefix $(DIR_BINS)/, $(BIN))
20 RMS+=$(BIN)
21 endif
22
23 ifneq ("$(LIB)", "")
24 LIB:=$(addprefix $(DIR_LIBS)/, $(LIB))
25 RMS+=$(LIB)
26 endif
27
28 SRC=$(wildcard *.c)
29 OBJ=$(SRC:.c=.o)
30 OBJ:=$(addprefix $(DIR_OBJS)/, $(OBJ))
31 DEP=$(SRC:.c=.dep)
32 DEP:=$(addprefix $(DIR_DEPS)/, $(DEP))
33
34 ifeq ("$(wildcard $(DIR_OBJS))", "")
35 DEP_DIR_OBJS := $(DIR_OBJS)
36 endif
37
38 ifeq ("$(wildcard $(DIR_BINS))", "")
39 DEP_DIR_BINS := $(DIR_BINS)
40 endif
41
42 ifeq ("$(wildcard $(DIR_DEPS))", "")
43 DEP_DIR_DEPS := $(DIR_DEPS)
44 endif
45
46 ifeq ("$(wildcard $(DIR_LIBS))", "")
47 DEP_DIR_LIBS := $(DIR_LIBS)
48 endif
49
50 ifneq ($(INCLUDE_DIRS), "")
51 INCLUDE_DIRS:=$(strip $(INCLUDE_DIRS))
52 INCLUDE_DIRS:=$(addprefix -I, $(INCLUDE_DIRS))
53 endif
54
55 ifneq ($(LINK_LIBS), "")
56 LINK_LIBS:=$(strip $(LINK_LIBS))
57 LIB_ALL:=$(notdir $(wildcard $(DIR_LIBS)/*))    #find out all the filename in "$(DIR_LIBS)/"
58                                                 #TEST_LIB:=$(addprefix $(DIR_LIBS)/, $(LIB_ALL))
59 LIB_FILTERED:=$(addsuffix .%, $(addprefix lib, $(LINK_LIBS))) # result is libxxx.%
60 $(eval DEP_LIBS=$(filter $(LIB_FILTERED), $(LIB_ALL)))
61 DEP_LIBS:=$(addprefix $(DIR_LIBS)/, $(DEP_LIBS))
62 LINK_LIBS:=$(addprefix -l, $(LINK_LIBS))
63 endif
64
65 #first goal must allocate this position
66 all: $(BIN) $(LIB)
67
68 #this include directive will import rule, if "all: $(BIN) $(LIB)" after this include directive
69 #the default target will not be "all: $(BIN) $(LIB)"
70 ifneq ("$(MAKECMDGOALS)", "clean")
71 include $(DEP)
72 endif
73
74 $(DIRS):
75     $(MKDIR) [email protected]
76
77 $(BIN): $(DEP_DIR_BINS) $(OBJ) $(DEP_LIBS)
78     $(CC) -o [email protected] -L$(DIR_LIBS) $(filter %.o, $^) $(LINK_LIBS)
79
80 $(LIB): $(DEP_DIR_LIBS) $(OBJ)
81     $(AR) $(ARFLAGS) [email protected] $(filter %.o, $^)
82
83 $(DIR_OBJS)/%.o: $(DEP_DIR_OBJS) %.c
84     $(CC) -c -o [email protected] $(INCLUDE_DIRS) $(filter %.c, $^)
85
86 $(DIR_DEPS)/%.dep: $(DEP_DIR_DEPS) %.c
87     @echo "Creating [email protected] ..."
88     @set -e; 89     $(RM) $(RMFLAGS) [email protected]; 90     $(CC) -E -MM $(INCLUDE_DIRS) $(filter %.c, $^) > [email protected]; 91     sed ‘s,\(.*\)\.o[ :]*,objs/\1.o [email protected]: ,g‘ < [email protected] > [email protected]; 92     $(RM) $(RMFLAGS) [email protected]
93
94 clean:
95     $(RM) $(RMFLAGS) $(RMS)
96     

./build/make.rule

1 BIN=
2
3 LIB=libfoo.a
4
5 INCLUDE_DIRS=$(ROOT)/code/foo/inc
6
7 LINK_LIBS=
8
9 include $(ROOT)/build/make.rule

./code/foo/src/Makefile

1 BIN=huge
2
3 LIB=
4
5 INCLUDE_DIRS=$(ROOT)/code/foo/inc $(ROOT)/code/bar/inc
6 LINK_LIBS=foo bar
7
8 include $(ROOT)/build/make.rule

./code/huge/src/Makefile

三、其他

  1. make提供很多命令选项,其中--debug用来打印调试信息,对于理解make的具体执行过程很有帮助。

  2. 调试makefile时除了--debug外,还可以通过echo打印。

  3. make默认是会从隐式规则中开始查找,如果我们不想要使用隐式规则,使用-r,提高编译效率。

  4. 活用make:

     经常遇到项目在不同计算机间拷贝或通过ftp传输时,不同计算机的时间设置不同造成项目文件的时间戳是将来的时间。

     解决:在makefile添加touch目标,命令是:find $(ROOT) -exec touch {} \;

     -exec  参数后面跟的是command命令,它的终止是以;为结束标志的,所以这句命令后面的分号是不可缺少的,考虑到各个系统中分号会有不同的意义,所以前面加反斜杠。

   

  

时间: 2024-12-27 09:11:34

make总结(三):高级的相关文章

MySQL简单快速入门 (三)高级查询——JEPLUS软件快速开发平台

03.SQL高级查询_分组: 1).需求:一条查询,查询出每种商品的最高价格 2).分组的命令:group by 分组字段 3).实现上例: select category_id,max(price)  from product group by category_id; 查询顺序:先分组,再聚合 4).注意事项: 分组查询的结果最多只能包含:分组列,聚合结果,不能包含其他字段. 5).练习1: 需求:查询每个生产日期的商品的数量是多少? select  proDate,count(*)  fr

JavaScript 面向对象(三) —— 高级篇

JavaScript 面向对象(一) —— 基础篇 JavaScript 面向对象(二) —— 案例篇 一.json方式的面向对象 首先要知道,js中出现的东西都能够放到json中.关于json数据格式这里推荐一篇博客:JSON 数据格式 先看下json创建的简单对象:相比基础篇中的构造函数.原型等的创建方式,json方式简单方便:但是缺点很明显,如果想创建多个对象,那么会产生大量重复代码,不可取. JSON方式适用于只创建一个对象的情况,代码简介又优雅. 1 <!DOCTYPE html>

python学习笔记(三) - 高级特性

一. 切片 切片操作和java中的subList类似,就是获取一个子列表 比如L=['zhangsan','lisi','wangwu'] 那么L[0, 2]表示从索引0开始取,直到索引2(不含2), 正好2个元素.如果第一个索引为0, 还可以省略. 下面我们创建一个0-99的数列: L = range(100) 1. 获取前10个数: L[:10] 2. 获取后10个数: L[-10:]    # 倒数第一个元素的索引是-1 3. 获取前11-20个数: L[10:20] 4. 获取前10个数

MyBatis 入门到精通(三) 高级结果映射

MyBatis的创建基于这样一个思想:数据库并不是您想怎样就怎样的.虽然我们希望所有的数据库遵守第三范式或BCNF(修正的第三范式),但它们不是.如果有一个数据库能够完美映射到所有应用程序,也将是非常棒的,但也没有.结果集映射就是MyBatis为解决这些问题而提供的解决方案.例如,我们如何映射下面这条语句? [html] view plaincopy <select id="selectBlog_by_id" parameterType="int" resul

大数据之Shell编程(三) 高级文本处理文本

目录 1.命令cut 2.命令sort 1.命令cut cut命令用于对文本进行切割 - 基本用法: 用法:cut [选项]... [文件]... 从每个文件中输出指定部分到标准输出. 长选项必须使用的参数对于短选项时也是必需使用的. -b, --bytes=列表 只选中指定的这些字节 -c, --characters=列表 只选中指定的这些字符 -d, --delimiter=分界符 使用指定分界符代替制表符作为区域分界 -f, --fields=LIST select only these

配置Log4j(非常具体)

来自: http://www.blogjava.net/zJun/archive/2006/06/28/55511.html Log4J的配置文件(Configuration File)就是用来设置记录器的级别.存放器和布局的,它可接key=value格式的设置或xml格式的设置信息.通过配置,能够创建出Log4J的执行环境. 1. 配置文件Log4J配置文件的基本格式例如以下: #配置根Loggerlog4j.rootLogger  =   [ level ]   ,  appenderNam

配置Log4j(很详细)【转】

来自: http://www.blogjava.net/zJun/archive/2006/06/28/55511.html Log4J的配置文件(Configuration File)就是用来设置记录器的级别.存放器和布局的,它可接key=value格式的设置或xml格式的设置信息.通过配置,可以创建出Log4J的运行环境. 1. 配置文件Log4J配置文件的基本格式如下: #配置根Loggerlog4j.rootLogger  =   [ level ]   ,  appenderName1

【Log4j】 log4j.properties 使用

一.参数意义说明 输出级别的种类 ERROR.WARN.INFO.DEBUG ERROR 为严重错误 主要是程序的错误 WARN 为一般警告,比如session丢失 INFO 为一般要显示的信息,比如登录登出 DEBUG 为程序的调试信息 配置日志信息输出目的地 log4j.appender.appenderName = fully.qualified.name.of.appender.class org.apache.log4j.ConsoleAppender(控制台) org.apache.

LOG4J.PROPERTIES配置详解(转载)

Log4J的配置文件(Configuration File)就是用来设置记录器的级别.存放器和布局的,它可接key=value格式的设置或xml格式的设置信息.通过配置,可以创建出Log4J的运行环境. 1. 配置文件Log4J配置文件的基本格式如下: #配置根Loggerlog4j.rootLogger = [ level ] , appenderName1 , appenderName2 , …#配置日志信息输出目的地Appenderlog4j.appender.appenderName =

awk入门及awk数组相关实战

知识点: l 记录与字段 l 模式匹配:模式与动作 l 基本的awk执行过程 l awk常用内置变量(预定义变量) l awk数组(工作常用) l awk语法:循环.条件 l awk常用函数 l 向awk传递参数 l awk引用shell变量 l awk小程序及调试思路 [[email protected] ~]# awk --version|head -1 GNU Awk 3.1.7 第1章 记录和字段 record记录==行, field字段相当于列,字段==列. awk对每个要处理的输入数