makefile--嵌套执行(四)
原创博文,转载请标明出处--周学伟http://www.cnblogs.com/zxouxuewei/
在大一些的项目里面,所有源代码不会只放在同一个目录,一般各个功能模块的源代码都是分开的,各自放在各自目录下,并且头文件和.c源文件也会有各 自的目录,这样便于项目代码的维护。这样我们可以在每个功能模块目录下都写一个Makefile,各自Makefile处理各自功能的编译链接工作,这样 我们就不必把所有功能的编译链接都放在同一个Makefile里面,这可使得我们的Makefile变得更加简洁,并且编译的时候可选择编译哪一个模块, 这对分块编译有很大的好处。
现在我所处于工程目录树如下:
. ├── include
│ ├── common.h
│ ├── ipc
│ │ └── ipc.h
│ └── tools
│ ├── base64.h
│ ├── md5.h
│ └── tools.h
├── Makefile
├── src
│ ├── ipc
│ │ ├── inc
│ │ ├── Makefile
│ │ └── src
│ │ └── ipc.c
│ ├── main
│ │ ├── inc
│ │ ├── Makefile
│ │ └── src
│ │ ├── main.c
│ │ └── main.c~
│ └── tools
│ ├── inc
│ ├── Makefile
│ └── src
│ ├── base64.c
│ ├── md5.c
│ └── tools.c
└── tags 13 directories, 16 files
这样组织项目源码要比之前合理一些,那这样怎么来写Makefile呢?我们可以在每个目录下写一个Makefile,通过最顶层的Makefile一层一层的向下嵌套执行各层Makefile。那么我们最顶层的Makefile简单点的话可以这样写:
# top Makefile for xxx all :
>---$(MAKE) -C src tags:
>---ctags -R clean :
>---$(MAKE) -C src clean .PHONY : all clean tags 命令: >---$(MAKE) -C src
就是进入src目录继续执行该目录下的Makefile。然后src目录下的Makefile在使用同样的方法进入下一级目录tools、 main、ipc,再执行该目录下的Makefile。其实这样有些麻烦,我们可以直接从顶层目录进入最后的目录执行make。再加入一些伪目标完善下, 我们的顶层Makefile就出来了:
# Top Makefile for C program # Copyright (C) 2014 shallnew \at 163 \dot com all :
>---$(MAKE) -C src/ipc
>---$(MAKE) -C src/tools
>---$(MAKE) -C src/main tags:
>---ctags -R help:
>---@echo "===============A common Makefilefor c programs=============="
>---@echo "Copyright (C) 2014 liuy0711 \at 163\dot com"
>---@echo "The following targets aresupport:"
>---@echo
>---@echo " all - (==make) compile and link"
>---@echo " obj - just compile, withoutlink"
>---@echo " clean - clean target"
>---@echo " distclean - clean target and otherinformation"
>---@echo " tags - create ctags for vimeditor"
>---@echo " help - print help information"
>---@echo
>---@echo "To make a target, do 'make[target]'"
>---@echo "========================= Version2.0 =======================" obj:
>---$(MAKE) -C src/ipc obj
>---$(MAKE) -C src/tools obj
>---$(MAKE) -C src/main obj clean :
>---$(MAKE) -C src/ipc clean
>---$(MAKE) -C src/tools clean
>---$(MAKE) -C src/main clean distclean:
>---$(MAKE) -C src/ipc distclean
>---$(MAKE) -C src/tools distclean
>---$(MAKE) -C src/main distclean .PHONY : all clean distclean tags help
当 我们这样组织源代码时,最下面层次的Makefile怎么写呢?肯定不可以将我们上一节的Makefile(version 1.1)直接拷贝到功能模块目录下,需要稍作修改。不能所有的模块都最终生成各自的可执行文件吧,我们目前是一个工程,所以最后只会生成一个可执行程序。 我们这样做,让主模块目录生成可执行文件,其他模块目录生成静态库文件,主模块链接时要用其他模块编译产生的库文件来生成最终的程序。将上一节 Makefile稍作修改得出编译库文件Makefile和编译可执行文件Makefile分别如下:
# A Makefile to generate archive file
# Copyright (C) 2014 shallnew \at 163 \dot com CFLAGS += -g -Wall -Werror -O2
CPPFLAGS += -I. -I./inc -I../../include # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
SRC_FILES = $(wildcard src/*.c)
SRC_OBJ = $(SRC_FILES:.c=.o)
SRC_LIB = libtools.a all : $(SRC_LIB) $(SRC_LIB) : $(SRC_OBJ)
>---$(AR) rcs $@ $^
>---cp $@ ../../libs obj : $(SRC_OBJ) # clean target
clean:
>---$(RM) $(SRC_OBJ) $(SRC_LIB) distclean:
>---$(RM) $(SRC_OBJ) $(SRC_LIB) tags *~ .PHONY : all obj clean disclean
==========================================================================
# A Makefile to generate executive file
# Copyright (C) 2014 shallnew \at 163 \dot com CFLAGS += -g -Wall -Werror -O2
CPPFLAGS += -I. -I./inc -I../../include
LDFLAGS += -lpthread -L../../libs -ltools -lipc # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
SRC_FILES = $(wildcard src/*.c)
SRC_OBJ = $(SRC_FILES:.c=.o)
SRC_BIN = target_bin all : $(SRC_BIN) $(SRC_BIN) : $(SRC_OBJ)
>---$(CC) -o $@ $^ $(LDFLAGS) obj : $(SRC_OBJ) # clean target
clean:
>---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe distclean:
>---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe tags*~ .PHONY : all obj clean disclean
最后在顶层执行:
# make clean make -C src/ipc clean
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
rm -f src/ipc.o libipc.a
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/ipc'
make -C src/tools clean
make[1]: Entering directory `/home/Myprojects/example_make/version-3.0/src/tools'
rm -f src/base64.o src/md5.o src/tools.o libtools.a
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
make -C src/main clean
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
rm -f src/main.o target_bin target_bin.exe
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
# make
make -C src/ipc
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/ipc.osrc/ipc.c
ar rcs libipc.a src/ipc.o
cp libipc.a ../../libs
make[1]: Leaving directory `/home/Myprojects/example_make/version-3.0/src/ipc'
make -C src/tools
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/tools'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/base64.osrc/base64.c
cc -g -Wall -Werror -O2 -I. -I./inc -I../../include -c -o src/md5.o src/md5.c
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/tools.osrc/tools.c
ar rcs libtools.a src/base64.o src/md5.o src/tools.o
cp libtools.a ../../libs
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
make -C src/main
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/main.osrc/main.c
cc -o target_bin src/main.o -lpthread -L../../libs -ltools-lipc
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
#
最后生成了可执行程序文件。这样的话一个工程的各个模块就变得独立出来了,不但源码分开了,而且各自有各自的Makefile,并且各个功能模块是可独立编译的。
我们发现顶层Makefile还有可以改进的地方,就是在进入下一层目录是要重复写多次,如下:
>---$(MAKE) -C src/ipc
>---$(MAKE) -C src/tools
>---$(MAKE) -C src/main
每增加一个目录都要在多个伪目标里面加入一行,这样不够自动化啊,于是我们想到shell的循环语 句,我们可以在每条规则的命令处使用for循环。如下:
DIR = src
SUBDIRS = $(shell ls $(DIR)) all :
>---@for subdir in $(SUBDIRS); \
>---do $(MAKE) -C $(DIR)/$$subdir; \
>---done
这样懒人有可以高兴很久了。不过还有问题:
上面for循环会依次进入系统命令ls列出的目录,但我们对每个目录的make顺序可能有要求,在该项目当中,main目录下的Makefile必 须最后执行,因为最终的链接需要其他目录编译生成的库文件,否则会执行失败。并且在当前的Makefile中,当子目录执行make出现错误时,make 不会退出。在最终执行失败的情况下,我们很难根据错误的提示定位出具体是是那个目录下的Makefile出现错误。这给问题定位造成了很大的困难。为了避 免这样的问题,在命令执行错误后make退出。
所以将刚才的Makefile修改为如下
DIR = src
SUBDIRS = $(shell ls $(DIR)) all :
>---@for subdir in $(SUBDIRS); \
>---do $(MAKE) -C $(DIR)/$$subdir || exit 1; \
>---done
这样在执行出错时立马退出,但这样还是没有解决问题,编译错误还是会出现。那怎么解决呢?
我们可以通过增加规则来限制make执行顺序,这样就要用到伪目标,对每一个模块我们都为他写一条规则,每个模块名称是目标,最后需要执行的模块目 标又是其他模块的目标,这样就限制了make顺序。在执行到最后需要执行的目标时,发现存在依赖,于是先更新依赖的目标,这样就不会出错了。并且这样的 话,我们还可以对指定模块进行编译,比如我只修改了tools模块,我只想看看我修改的这个模块代码是否可以编译通过,我可以在编译时这样:
# make tools
make -C src/tools
make[1]: Entering directory`/home/Myprojects/example_make/version-2.1/src/tools'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/base64.o src/base64.c
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/md5.osrc/md5.c
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/tools.osrc/tools.c
ar rcs libtools.a src/base64.o src/md5.o src/tools.o
cp libtools.a ../../libs
make[1]: Leaving directory`/home/Myprojects/example_make/version-2.1/src/tools'
#
还 有另外一种方法也可以解决此问题,就是手动列出需要进入执行的模块名称(这里就是目录了),把最后需要执行的模块放在最后,这样for循环执行时最后需要 编译链接的模块就放在最后了,不会像我们之前那样make是按照使用系统命令ls列出模块目录的顺序来执行。ls列出目录是按照每个目录的名称来排序的, 我们总不能要求写代码的时候最后执行的模块的名称必须是以z开头的吧,总之不现实。
我们的顶层Makefile又进化了,也是这一节最终Makefile
# Top Makefile for C program
# Copyright (C) 2014 shallnew \at 163 \dot com DIR = src
MODULES = $(shell ls $(DIR))
# MODULES = ipc main tools all : $(MODULES) $(MODULES):
>---$(MAKE) -C $(DIR)/$@ main:tools ipc obj:
>---@for subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir $@; \
>---done clean :
>---@for subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir $@; \
>---done distclean:
>---@for subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir $@; \
>---done tags:
>---ctags -R help:
>---@echo "===============A common Makefilefor c programs=============="
>---@echo "Copyright (C) 2014 liuy0711 \at 163\dot com"
>---@echo "The following targets aresupport:"
>---@echo
>---@echo " all - (==make) compile and link"
>---@echo " obj - just compile, withoutlink"
>---@echo " clean - clean target"
>---@echo " distclean - clean target and otherinformation"
>---@echo " tags - create ctags for vimeditor"
>---@echo " help - print help information"
>---@echo
>---@echo "To make a target, do 'make[target]'"
>---@echo "========================= Version2.0 =======================" .PHONY : all clean distclean tags help
makefile--嵌套执行(四)的更多相关文章
- makefile详解 嵌套执行make,定义命令包
嵌套执行make 在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而 ...
- Makefile之嵌套执行make
1.在Makefile中 一般使用一个总控Makefile来指明文件的编译规则 如: subsystem: cd subdir && $(MAKE) 其等价于 subsystem: $ ...
- 跟我一起写 Makefile(十四)
使用make更新函数库文件 ----------- 函数库文件也就是对Object文件(程序编译的中间文件)的打包文件.在Unix下,一般是由命令"ar"来完成打包工作. 一.函数 ...
- ACID 数据库正确执行四要素
ACID:数据库事务正确执行所必须满足的四个基本要素的缩写: 原子性(atomicity,或叫不可分割性),一致性(consistency),隔离性(isolation,又称独立性),持久性(dura ...
- makefile 嵌套
目录结构: dynamic 和 static 两个目录实现加法功能, 分别生成动态库和静态库, main.c 主文件链接加法功能, 终端命令执行: make dynamic=1 则编译动态 ...
- day10 函数的嵌套执行顺序
函数嵌套的执行顺序,从上往下,一层一层的执行重点注意的是函数和内存的交互原理 NMAE = def yangtuo(): # 2 将整个函数放入内存编译,但是不执行 name = "yang ...
- for循环嵌套执行效率
今天做项目时遇到一个for循环的嵌套问题,一个循环次数多,一个次数少,怎样设计效率较高. 想起以前笔试时遇到过这个问题,当时由于时间仓促,没有细想,今天在实际代码中遇到这么问题,于是动笔算了下. 设两 ...
- Contiki 2.7 Makefile 文件(四)
3.第三部分 这里我们假设TARGET为native (1) OBJECTDIR = obj_$(TARGET) LOWERCASE = -abcdefghijklmnopqrstuvwxyz UPP ...
- shell中嵌套执行expect命令实例(利用expect实现自动登录)
expect是 #!/bin/bashpasswd='123456'/usr/bin/expect <<EOFset time 30spawn ssh root@192.168.76.10 ...
- Linux makefile教程之书写命令四[转]
书写命令———— 每 条规则中的命令和操作系统Shell的命令行是一致的.make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟 在依赖规则后面的分号后的.在命令 ...
随机推荐
- Oracle动态执行表不可访问解决方法
在scott 用户下,执行查询语句是出现“Oracle动态执行表不可访问” 经查,是因为用户权限不够所致,修改scott用户权限语句如下: grant select on V_$session to ...
- linux命令(33):less
一.less指令 less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大.less 的用法比起 more 更加的有弹性. 在 more 的时候,我 ...
- PairRDD中算子foldByKey图解
foldByKey 函数原型: def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)] def foldByKey(zeroVal ...
- Tomcat: Could not clean server of obsolete files
<Context path="/mldn" docBase="E:/web.workspace/mldndemo/WebContent" reloadab ...
- Hdu1163 Eddy's digitai Roots(九余数定理)
题目大意: 给定一个正整数,根据一定的规则求出该数的“数根”,其规则如下: 例如给定 数字 24,将24的各个位上的数字“分离”,分别得到数字 2 和 4,而2+4=6: 因为 6 < 10,所 ...
- biicode:一个现代的 C 依赖管理器
因为经营原因,公司已经倒闭了. Biicode (just the company) post-mortemPosted on August 11, 2015 by biicode TeamThis ...
- Java程序员应该了解的10个设计原则
引用: http://www.cnblogs.com/leehongee/archive/2012/03/18/2404760.html 面向对象设计原则是OOPS(Object-Oriented P ...
- 纯css3实现的超炫checkbox复选框和radio单选框
之前为大家分享了好多css3实现的按钮.今天要为大家分享的是纯css3实现的checkbox复选框和radio单选框,效果超级炫.先让我们看看图吧! 在线预览 源码下载 这个实例完全由css3实现 ...
- 解决android studio项目中Failded to sync Gradle project 'XXXX' Cause:failed to find target with hash string 'android-16'问题
之前在github上通过import module导入一个项目,结果报错,提示找不到sdk相应的版本xx,而我的compileSdkVersion明明写的是23不是xx,查了半天也没解决.最后只好下载 ...
- prometheus报警消息钉钉通知
设置prometheus 的web hook 为对应服务: 报警的配置如下,设置了web hook url,报警就会把消息发给web hookurl,但是这里的数据格式和钉钉要求的格式不一样,所以后面 ...