从头開始写项目Makefile(五):嵌套运行
【版权声明:转载请保留出处:blog.csdn.net/gentleliu。Mail:shallnew at 163 dot com】
在大一些的项目里面,全部源码不会仅仅放在同一个文件夹,一般各个功能模块的源码都是分开的,各自放在各自文件夹下。而且头文件和.c源文件也会有各自的文件夹。这样便于项目代码的维护。这样我们能够在每一个功能模块文件夹下都写一个Makefile,各自Makefile处理各自功能的编译链接工作,这样我们就不必把全部功能的编译链接都放在同一个Makefile里面,这可使得我们的Makefile变得更加简洁,而且编译的时候可选择编译哪一个模块,这对分块编译有非常大的优点。
如今我所处于project文件夹树例如以下:
- .
- ├── 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)直接复制到功能模块文件夹下,须要稍作改动。
不能全部的模块都终于生成各自的可运行文件吧,我们眼下是一个project,所以最后仅仅会生成一个可运行程序。我们这样做,让主模块文件夹生成可运行文件。其它模块文件夹生成静态库文件,主模块链接时要用其它模块编译产生的库文件来生成终于的程序。将上一节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'
- #
最后生成了可运行程序文件。这种话一个project的各个模块就变得独立出来了,不但源代码分开了,并且各自有各自的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命令显示
[版权声明:转载请保留出处:blog.csdn.net/gentleliu.Mail:shallnew at 163 dot com] 这一节我们讲一下make的函数,在之前的章节已经讲到了几 ...
- 从头開始写项目Makefile(七):统一目标输出文件夹
[版权声明:转载请保留出处:blog.csdn.net/gentleliu. Mail:shallnew at 163 dot com] 上一节我们把规则单独提取出来,方便了Makefile的 ...
- 从0開始写MyScrollView
从0開始写MyScrollView 上篇文章对ScrollView的详细实现进行了分析.本文依据上篇分析的结果.自己动手写一个ScrollView. step1 尾随手指滑动,非常easy.重写2个函 ...
- [php learn] php 从头開始学习1
前言:大概在2006年的时候,学习过一段时间的php.而且当时做了一个下载的站点,后来因为读研究生阶段用的是java.j2ee相关,所以php就搁浅掉了,php这些年也发生了非常大的变化,最大一个变化 ...
- 从零開始写游戏引擎(一) - project创建以及文件夹设置还有版本号控制
一句话提要 好的開始等于成功了一半. 创建文件夹结构 project文件夹下最好分为以下几个文件夹 Docs - 开发文档,设计文档 Assets - 角色,动作,模型和音效等 Source - 代码 ...
- SQL从头開始
SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL) 查询和更新指令构成了 SQL 的 DML 部分: SELECT - 从数据库表中获取数据 UPDATE - 更新数据库表中 ...
- [Golang] 从零開始写Socket Server(3): 对长、短连接的处理策略(模拟心跳)
通过前两章,我们成功是写出了一套凑合能用的Server和Client,并在二者之间实现了通过协议交流.这么一来,一个简易的socket通讯框架已经初具雏形了,那么我们接下来做的.就是想办法让这个框架更 ...
- 从头開始学 RecyclerView(六) LayoutManager
前言 在前面的文章中.每一个演示样例,都使用了LayoutManager,毕竟它是RecyclerView必不可少的一部分. LayoutManager,顾名思义,就是『布局管理器』. 使用例如以下代 ...
- 从头开始写项目Makefile(十):make内嵌函数及make命令显示【转】
转自:http://blog.csdn.net/shallnet/article/details/38314473#comments 版权声明:本文为博主原创文章,未经博主允许不得转载.如果您觉得文章 ...
随机推荐
- [openjudge6043]哆啦A梦的时光机
[openjudge6043]哆啦A梦的时光机 试题描述 哆啦A梦有一个神奇的道具:时光机.坐着它,大雄和他的伙伴们能穿越时空,回到过去或者去到未来. 有一天,大雄和他的伙伴们想穿越时空进行探险,可是 ...
- BZOJ1709超级弹珠
1709: [Usaco2007 Oct]Super Paintball超级弹珠 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 352 Solved: ...
- 乌班图中的ssh服务
SSH服务(TCP端口号22):安全的命令解释器 为客户机提供安全的Shell 环境,用于远程管理 ...
- [LeetCode] Binary Search Tree Iterator 深度搜索
Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the ro ...
- jdk、maven、tomcat环境变量配置
1.jdk 新建环境变量: JAVA_HOME:C:\Program Files\Java\jdk1.8.0_91 CLASSPATH:.;%JAVA_HOME%\lib;%JAVA_HOME%\li ...
- AC日记——[Ahoi2009]Seq 维护序列seq bzoj 1798
1798 思路: 维护两个标记: 乘:m 和 加:a 先下放乘,再下放加: 下放乘的时候要把子节点的加一块乘了: 开long long: 来,上代码: #include <cstdio> ...
- AC日记——郁闷的出纳员 codevs 1286
郁闷的出纳员 思路: 设工资下限为ko,然后ko--(因为要小于工资下限): 设cur为记录工资增长,降低: 设第i个人的工资为pos: 对应的四种操作: 插入:cur-pos-ko: 增长:cur- ...
- DOM-window下的常用子对象-location-刷新页面
1.刷新当前页面:(通过给location.href赋值的方式) window.location.href="" eg:window.location.href="htt ...
- jemalloc原理分析
netty4引入了内存池的概念,它的主要思想源自于jemalloc,由于难以理解netty中这一块的代码,我决定先看一看网上的相关文章 官方git jemalloc原理分析 jemalloc和内存管理 ...
- python-re之中文匹配
#coding=utf-8 import re import chardet#检测网页编码形式的模块 p = re.compile(r'\d+') print p.findall('one1two2t ...