【版权声明:转载请保留出处:blog.csdn.net/gentleliu。Mail:shallnew at 163 dot com】

在大一些的项目里面,全部源码不会仅仅放在同一个文件夹,一般各个功能模块的源码都是分开的,各自放在各自文件夹下。而且头文件和.c源文件也会有各自的文件夹。这样便于项目代码的维护。这样我们能够在每一个功能模块文件夹下都写一个Makefile,各自Makefile处理各自功能的编译链接工作,这样我们就不必把全部功能的编译链接都放在同一个Makefile里面,这可使得我们的Makefile变得更加简洁,而且编译的时候可选择编译哪一个模块,这对分块编译有非常大的优点。

如今我所处于project文件夹树例如以下:

  1. .
  2.  
  3. ├── include
  4. ├── common.h
  5. ├── ipc
  6. └── ipc.h
  7. └── tools
  8. ├── base64.h
  9. ├── md5.h
  10. └── tools.h
  11. ├── Makefile
  12. ├── src
  13. ├── ipc
  14. ├── inc
  15. ├── Makefile
  16. └── src
  17. └── ipc.c
  18. ├── main
  19. ├── inc
  20. ├── Makefile
  21. └── src
  22. ├── main.c
  23. └── main.c~
  24. └── tools
  25. ├── inc
  26. ├── Makefile
  27. └── src
  28. ├── base64.c
  29. ├── md5.c
  30. └── tools.c
  31. └── tags
  32.  
  33. 13 directories, 16 files

这样组织项目源代码要比之前合理一些。那这样怎么来写Makefile呢?我们能够在每一个文件夹下写一个Makefile,通过最顶层的Makefile一层一层的向下嵌套运行各层Makefile。那么我们最顶层的Makefile简单点的话能够这样写:

  1. # top Makefile for xxx
  2.  
  3. all :
  4. >---$(MAKE) -C src
  5.  
  6. tags:
  7. >---ctags -R
  8.  
  9. clean :
  10. >---$(MAKE) -C src clean
  11.  
  12. .PHONY : all clean tags

命令:

>---$(MAKE) -C src

就是进入src文件夹继续运行该文件夹下的Makefile。然后src文件夹下的Makefile在使用相同的方法进入下一级文件夹tools、main、ipc。再运行该文件夹下的Makefile。事实上这样有些麻烦。我们能够直接从顶层文件夹进入最后的文件夹运行make。再增加一些伪目标完好下。我们的顶层Makefile就出来了:

  1. # Top Makefile for C program
  2.  
  3. # Copyright (C) 2014 shallnew \at 163 \dot com
  4.  
  5. all :
  6. >---$(MAKE) -C src/ipc
  7. >---$(MAKE) -C src/tools
  8. >---$(MAKE) -C src/main
  9.  
  10. tags:
  11. >---ctags -R
  12.  
  13. help:
  14. >---@echo "===============A common Makefilefor c programs=============="
  15. >---@echo "Copyright (C) 2014 liuy0711 \at 163\dot com"
  16. >---@echo "The following targets aresupport:"
  17. >---@echo
  18. >---@echo " all - (==make) compile and link"
  19. >---@echo " obj - just compile, withoutlink"
  20. >---@echo " clean - clean target"
  21. >---@echo " distclean - clean target and otherinformation"
  22. >---@echo " tags - create ctags for vimeditor"
  23. >---@echo " help - print help information"
  24. >---@echo
  25. >---@echo "To make a target, do 'make[target]'"
  26. >---@echo "========================= Version2.0 ======================="
  27.  
  28. obj:
  29. >---$(MAKE) -C src/ipc obj
  30. >---$(MAKE) -C src/tools obj
  31. >---$(MAKE) -C src/main obj
  32.  
  33. clean :
  34. >---$(MAKE) -C src/ipc clean
  35. >---$(MAKE) -C src/tools clean
  36. >---$(MAKE) -C src/main clean
  37.  
  38. distclean:
  39. >---$(MAKE) -C src/ipc distclean
  40. >---$(MAKE) -C src/tools distclean
  41. >---$(MAKE) -C src/main distclean
  42.  
  43. .PHONY : all clean distclean tags help

当我们这样组织源码时。最以下层次的Makefile怎么写呢?肯定不能够将我们上一节的Makefile(version 1.1)直接复制到功能模块文件夹下,须要稍作改动。

不能全部的模块都终于生成各自的可运行文件吧,我们眼下是一个project,所以最后仅仅会生成一个可运行程序。我们这样做,让主模块文件夹生成可运行文件。其它模块文件夹生成静态库文件,主模块链接时要用其它模块编译产生的库文件来生成终于的程序。将上一节Makefile稍作改动得出编译库文件Makefile和编译可运行文件Makefile分别例如以下:

  1. # A Makefile to generate archive file
  2. # Copyright (C) 2014 shallnew \at 163 \dot com
  3.  
  4. CFLAGS += -g -Wall -Werror -O2
  5. CPPFLAGS += -I. -I./inc -I../../include
  6.  
  7. # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
  8. SRC_FILES = $(wildcard src/*.c)
  9. SRC_OBJ = $(SRC_FILES:.c=.o)
  10. SRC_LIB = libtools.a
  11.  
  12. all : $(SRC_LIB)
  13.  
  14. $(SRC_LIB) : $(SRC_OBJ)
  15. >---$(AR) rcs $@ $^
  16. >---cp $@ ../../libs
  17.  
  18. obj : $(SRC_OBJ)
  19.  
  20. # clean target
  21. clean:
  22. >---$(RM) $(SRC_OBJ) $(SRC_LIB)
  23.  
  24. distclean:
  25. >---$(RM) $(SRC_OBJ) $(SRC_LIB) tags *~
  26.  
  27. .PHONY : all obj clean disclean

==========================================================================

  1. # A Makefile to generate executive file
  2. # Copyright (C) 2014 shallnew \at 163 \dot com
  3.  
  4. CFLAGS += -g -Wall -Werror -O2
  5. CPPFLAGS += -I. -I./inc -I../../include
  6. LDFLAGS += -lpthread -L../../libs -ltools -lipc
  7.  
  8. # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
  9. SRC_FILES = $(wildcard src/*.c)
  10. SRC_OBJ = $(SRC_FILES:.c=.o)
  11. SRC_BIN = target_bin
  12.  
  13. all : $(SRC_BIN)
  14.  
  15. $(SRC_BIN) : $(SRC_OBJ)
  16. >---$(CC) -o $@ $^ $(LDFLAGS)
  17.  
  18. obj : $(SRC_OBJ)
  19.  
  20. # clean target
  21. clean:
  22. >---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe
  23.  
  24. distclean:
  25. >---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe tags*~
  26.  
  27. .PHONY : all obj clean disclean

最后在顶层运行:

  1. # make clean
  2.  
  3. make -C src/ipc clean
  4. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
  5. rm -f src/ipc.o libipc.a
  6. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/ipc'
  7. make -C src/tools clean
  8. make[1]: Entering directory `/home/Myprojects/example_make/version-3.0/src/tools'
  9. rm -f src/base64.o src/md5.o src/tools.o libtools.a
  10. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
  11. make -C src/main clean
  12. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
  13. rm -f src/main.o target_bin target_bin.exe
  14. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
  15. # make
  16. make -C src/ipc
  17. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
  18. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/ipc.osrc/ipc.c
  19. ar rcs libipc.a src/ipc.o
  20. cp libipc.a ../../libs
  21. make[1]: Leaving directory `/home/Myprojects/example_make/version-3.0/src/ipc'
  22. make -C src/tools
  23. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/tools'
  24. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/base64.osrc/base64.c
  25. cc -g -Wall -Werror -O2 -I. -I./inc -I../../include -c -o src/md5.o src/md5.c
  26. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/tools.osrc/tools.c
  27. ar rcs libtools.a src/base64.o src/md5.o src/tools.o
  28. cp libtools.a ../../libs
  29. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
  30. make -C src/main
  31. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
  32. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/main.osrc/main.c
  33. cc -o target_bin src/main.o -lpthread -L../../libs -ltools-lipc
  34. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
  35. #

最后生成了可运行程序文件。这种话一个project的各个模块就变得独立出来了,不但源代码分开了,并且各自有各自的Makefile,并且各个功能模块是可独立编译的。

我们发现顶层Makefile还有能够改进的地方,就是在进入下一层文件夹是要反复写多次,例如以下:

  1. >---$(MAKE) -C src/ipc
  2. >---$(MAKE) -C src/tools
  3. >---$(MAKE) -C src/main

每添加一个文件夹都要在多个伪目标里面添加一行。这样不够自己主动化啊,于是我们想到shell的循环语 句,我们能够在每条规则的命令处使用for循环。

例如以下:

  1. DIR = src
  2. SUBDIRS = $(shell ls $(DIR))
  3.  
  4. all :
  5. >---@for subdir in $(SUBDIRS); \
  6. >---do $(MAKE) -C $(DIR)/$$subdir; \
  7. >---done

这样懒人有能够高兴非常久了。

只是还有问题:

上面for循环会依次进入系统命令ls列出的文件夹,但我们对每一个文件夹的make顺序可能有要求,在该项目其中。main文件夹下的Makefile必须最后运行,由于终于的链接须要其它文件夹编译生成的库文件,否则会运行失败。

而且在当前的Makefile中,当子文件夹运行make出现错误时。make不会退出。在终于运行失败的情况下,我们非常难依据错误的提示定位出详细是是那个文件夹下的Makefile出现错误。这给问题定位造成了非常大的困难。为了避免这种问题,在命令运行错误后make退出。

所以将刚才的Makefile改动为例如以下

  1. DIR = src
  2. SUBDIRS = $(shell ls $(DIR))
  3.  
  4. all :
  5. >---@for subdir in $(SUBDIRS); \
  6. >---do $(MAKE) -C $(DIR)/$$subdir || exit 1; \
  7. >---done

这样在运行出错时立刻退出,但这样还是没有解决这个问题。编译错误还是会出现。

那怎么解决呢?

我们能够通过添加规则来限制make运行顺序,这样就要用到伪目标,对每个模块我们都为他写一条规则,每个模块名称是目标,最后须要运行的模块目标又是其它模块的目标,这样就限制了make顺序。在运行到最后须要运行的目标时,发现存在依赖,于是先更新依赖的目标,这样就不会出错了。而且这种话,我们还能够对指定模块进行编译,比方我仅仅改动了tools模块,我仅仅想看看我改动的这个模块代码能否够编译通过,我能够在编译时这样:

  1. # make tools
  2. make -C src/tools
  3. make[1]: Entering directory`/home/Myprojects/example_make/version-2.1/src/tools'
  4. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/base64.o src/base64.c
  5. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/md5.osrc/md5.c
  6. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/tools.osrc/tools.c
  7. ar rcs libtools.a src/base64.o src/md5.o src/tools.o
  8. cp libtools.a ../../libs
  9. make[1]: Leaving directory`/home/Myprojects/example_make/version-2.1/src/tools'
  10. #

还有第二种方法也能够解决此问题,就是手动列出须要进入运行的模块名称(这里就是文件夹了)。把最后须要运行的模块放在最后,这样for循环运行时最后须要编译链接的模块就放在最后了,不会像我们之前那样make是依照使用系统命令ls列出模块文件夹的顺序来运行。

ls列出文件夹是依照每一个文件夹的名称来排序的,我们总不能要求写代码的时候最后运行的模块的名称必须是以z开头的吧。总之不现实。

我们的顶层Makefile又进化了,也是这一节终于Makefile:

  1. # Top Makefile for C program
  2. # Copyright (C) 2014 shallnew \at 163 \dot com
  3.  
  4. DIR = src
  5. MODULES = $(shell ls $(DIR))
  6. # MODULES = ipc main tools
  7.  
  8. all : $(MODULES)
  9.  
  10. $(MODULES):
  11. >---$(MAKE) -C $(DIR)/$@
  12.  
  13. main:tools ipc
  14.  
  15. obj:
  16. >---@for subdir in $(MODULES); \
  17. >---do $(MAKE) -C $(DIR)/$$subdir $@; \
  18. >---done
  19.  
  20. clean :
  21. >---@for subdir in $(MODULES); \
  22. >---do $(MAKE) -C $(DIR)/$$subdir $@; \
  23. >---done
  24.  
  25. distclean:
  26. >---@for subdir in $(MODULES); \
  27. >---do $(MAKE) -C $(DIR)/$$subdir $@; \
  28. >---done
  29.  
  30. tags:
  31. >---ctags -R
  32.  
  33. help:
  34. >---@echo "===============A common Makefilefor c programs=============="
  35. >---@echo "Copyright (C) 2014 liuy0711 \at 163\dot com"
  36. >---@echo "The following targets aresupport:"
  37. >---@echo
  38. >---@echo " all - (==make) compile and link"
  39. >---@echo " obj - just compile, withoutlink"
  40. >---@echo " clean - clean target"
  41. >---@echo " distclean - clean target and otherinformation"
  42. >---@echo " tags - create ctags for vimeditor"
  43. >---@echo " help - print help information"
  44. >---@echo
  45. >---@echo "To make a target, do 'make[target]'"
  46. >---@echo "========================= Version2.0 ======================="
  47.  
  48. .PHONY : all clean distclean tags help

从头開始写项目Makefile(五):嵌套运行的更多相关文章

  1. 从头開始写项目Makefile(十):make内嵌函数及make命令显示

    [版权声明:转载请保留出处:blog.csdn.net/gentleliu.Mail:shallnew at 163 dot com]     这一节我们讲一下make的函数,在之前的章节已经讲到了几 ...

  2. 从头開始写项目Makefile(七):统一目标输出文件夹

    [版权声明:转载请保留出处:blog.csdn.net/gentleliu. Mail:shallnew at 163 dot com]     上一节我们把规则单独提取出来,方便了Makefile的 ...

  3. 从0開始写MyScrollView

    从0開始写MyScrollView 上篇文章对ScrollView的详细实现进行了分析.本文依据上篇分析的结果.自己动手写一个ScrollView. step1 尾随手指滑动,非常easy.重写2个函 ...

  4. [php learn] php 从头開始学习1

    前言:大概在2006年的时候,学习过一段时间的php.而且当时做了一个下载的站点,后来因为读研究生阶段用的是java.j2ee相关,所以php就搁浅掉了,php这些年也发生了非常大的变化,最大一个变化 ...

  5. 从零開始写游戏引擎(一) - project创建以及文件夹设置还有版本号控制

    一句话提要 好的開始等于成功了一半. 创建文件夹结构 project文件夹下最好分为以下几个文件夹 Docs - 开发文档,设计文档 Assets - 角色,动作,模型和音效等 Source - 代码 ...

  6. SQL从头開始

    SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL) 查询和更新指令构成了 SQL 的 DML 部分: SELECT - 从数据库表中获取数据 UPDATE - 更新数据库表中 ...

  7. [Golang] 从零開始写Socket Server(3): 对长、短连接的处理策略(模拟心跳)

    通过前两章,我们成功是写出了一套凑合能用的Server和Client,并在二者之间实现了通过协议交流.这么一来,一个简易的socket通讯框架已经初具雏形了,那么我们接下来做的.就是想办法让这个框架更 ...

  8. 从头開始学 RecyclerView(六) LayoutManager

    前言 在前面的文章中.每一个演示样例,都使用了LayoutManager,毕竟它是RecyclerView必不可少的一部分. LayoutManager,顾名思义,就是『布局管理器』. 使用例如以下代 ...

  9. 从头开始写项目Makefile(十):make内嵌函数及make命令显示【转】

    转自:http://blog.csdn.net/shallnet/article/details/38314473#comments 版权声明:本文为博主原创文章,未经博主允许不得转载.如果您觉得文章 ...

随机推荐

  1. [openjudge6043]哆啦A梦的时光机

    [openjudge6043]哆啦A梦的时光机 试题描述 哆啦A梦有一个神奇的道具:时光机.坐着它,大雄和他的伙伴们能穿越时空,回到过去或者去到未来. 有一天,大雄和他的伙伴们想穿越时空进行探险,可是 ...

  2. BZOJ1709超级弹珠

    1709: [Usaco2007 Oct]Super Paintball超级弹珠 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 352  Solved:  ...

  3. 乌班图中的ssh服务

    SSH服务(TCP端口号22):安全的命令解释器                                     为客户机提供安全的Shell 环境,用于远程管理                ...

  4. [LeetCode] Binary Search Tree Iterator 深度搜索

    Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the ro ...

  5. jdk、maven、tomcat环境变量配置

    1.jdk 新建环境变量: JAVA_HOME:C:\Program Files\Java\jdk1.8.0_91 CLASSPATH:.;%JAVA_HOME%\lib;%JAVA_HOME%\li ...

  6. AC日记——[Ahoi2009]Seq 维护序列seq bzoj 1798

    1798 思路: 维护两个标记: 乘:m  和  加:a 先下放乘,再下放加: 下放乘的时候要把子节点的加一块乘了: 开long long: 来,上代码: #include <cstdio> ...

  7. AC日记——郁闷的出纳员 codevs 1286

    郁闷的出纳员 思路: 设工资下限为ko,然后ko--(因为要小于工资下限): 设cur为记录工资增长,降低: 设第i个人的工资为pos: 对应的四种操作: 插入:cur-pos-ko: 增长:cur- ...

  8. DOM-window下的常用子对象-location-刷新页面

    1.刷新当前页面:(通过给location.href赋值的方式) window.location.href="" eg:window.location.href="htt ...

  9. jemalloc原理分析

    netty4引入了内存池的概念,它的主要思想源自于jemalloc,由于难以理解netty中这一块的代码,我决定先看一看网上的相关文章 官方git jemalloc原理分析 jemalloc和内存管理 ...

  10. python-re之中文匹配

    #coding=utf-8 import re import chardet#检测网页编码形式的模块 p = re.compile(r'\d+') print p.findall('one1two2t ...