原创博文,转载请标明出处--周学伟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

转载于:https://www.cnblogs.com/zxouxuewei/p/5107556.html

makefile--嵌套执行(四)相关推荐

  1. 浅显易懂 Makefile 入门 (10)— 嵌套执行 make、export 的使用

    1. 嵌套执行 make 在一个大的工程文件中,不同的文件按照功能被划分到不同的模块中,每个模块可能都会有自己的编译顺序和规则,如果在一个 Makefile 文件中描述所有模块的编译规则,就会很乱,执 ...

  2. Makefile之嵌套执行(9)

    在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西 ...

  3. Makefile文件(四)_书写命令

    变量说明: $@       --->      目标文件 $^       --->       所有的依赖文件 $<       --->       第一个依赖文件 一. ...

  4. java循环的嵌套执行

    java循环的嵌套执行 /*** 循环的嵌套执行*/ public class Test14 {public static void main(String[] args) {/*** 外层循环执行一 ...

  5. 高效能人士的执行四原则(一)——前言

    最近阅读了<高效能人士的执行四原则>,感叹其通俗易懂,且具有良好的可实践性.这是一本讲管理的书,对"个人管理""团队管理""组织管理&qu ...

  6. 高效能人士的执行四原则(二)——原则1:聚焦最重要目标

    1.什么是聚焦最重要目标     将你最好的精力集中到一两个最重要的目标上来,而非平均地放到十几个目标上去--这一两个目标应该是能够使你得到革命性结果的事情. 聚焦,并非要缩减日常事务的规模和复杂度, ...

  7. java 双重for循环_Java--for循环之双层循环嵌套 执行流程

    for循环单层循环执行流程:http://blog.csdn.net/asheandwine/article/details/76383304 接下来看双层循环: 循环嵌套是指在一个循环语句中再定义一 ...

  8. Java--for循环之双层循环嵌套 执行流程

    for循环单层循环执行流程:http://blog.csdn.net/asheandwine/article/details/76383304 接下来看双层循环: 循环嵌套是指在一个循环语句中再定义一 ...

  9. 万能makefile深入浅出 - 第四篇

    1. 本示例演示的是编译多个可执行程序,库文件,需链接动态库静态库,且需先编译库,并且库与库之间,可执行程序之间皆存在依赖关系的makefile的编写方式(自己写的简单动态库编译和使用,自己写的简单静 ...

  10. DVWA--Command Injection(命令执行)--四个等级

    Command Injection,也就是我们常说的命令执行,DVWA共有四个等级 索引目录: Low Medium High Impossible 命令执行漏洞的原理: :在操作系统中, & ...

最新文章

  1. android.mk 里面内容介绍
  2. 2019金融科技风往哪儿吹?蚂蚁金服联合20余家金融机构预测新年热点:5G、区块链上榜...
  3. GDI+ 中Image::FromStream ,用流的方式显示图像
  4. 解决Jsp运行org.apache.jasper.JasperException: Unable to compile class for JSP:问题
  5. Html轻松使用拖拽实现垃圾桶,代码带注释,包你能看懂
  6. 云上如何做冷热数据分离
  7. 反应式服务中的线程本地状态可用性
  8. jQuery-input输入框下拉提示层
  9. cocos中如何让背景模糊_cocos2dx-js Shader的使用(高斯模糊)
  10. 1核1g java_wordpress博客1核1G1M够用吗
  11. 20191004每日一句
  12. Android 触摸事件转换为鼠标事件
  13. 国际C 语言乱码大赛(IOCCC )
  14. android 分辨率 720 1184 适配,墨迹天气 Android版最新!趋势界面可以横向滑动切换城市!...
  15. 在线web表单设计器
  16. 电脑链接打印机方法(TCP/IP连接,Windows10适用,其他版本未验证)
  17. Oracle drop table
  18. android点击vcf文件,Android vcard使用示例,生成vcf文件
  19. LNK1104:无法打开文件d3dx11.lib
  20. 在线网络打字系统_打字比赛系统软件

热门文章

  1. Algs4-1.3.45栈的可生成性
  2. 移动端ios和安卓input问题
  3. 【CodeForces】947 D. Picking Strings
  4. makefile高级应用
  5. Ubuntu虚拟机及Petalinux开发平台安装
  6. SQL Server 内存泄露(memory leak)——游标导致的内存问题
  7. Python学生成绩处理专业版
  8. python msi installer_Windows10 MYSQL Installer 安装(mysql-installer-community-5.7.19.0.msi)
  9. html整体移动,html 可以拖动多个div
  10. softether linux 客户端,SoftEther centos 服务器搭建 及 客户端连接