今天写了个层次化的Makefile模版,用来自动化编译项目,这个模版应当包含以下功能:

  • 适用于层次化结构,Makefile主要内容都放在顶层目录下的Makefile.env中,子层Makefile包含这个Makefile.env,只要增加一些变量就可以编译,特别方便添加新的功能模块
  • 自动解析头文件依赖

我的程序的目录结构是这样的:

1. 源文件目录src,模块xxx放在src/xxx下,主程序在src/main下面

2.公共头文件放在include目录下,模块xxx的头文件放在include/xxx目录下

3.模块输出的链接库放在lib目录下

4.可执行文件放在bin目录下

先来看一下Makefile.env,这个类似于c的头文件,包含了所有Makefile的公共部分,

###########  MakeFile.env  ##########
# Top level pattern, include by Makefile of child directory
# in which variable like TOPDIR, TARGET or LIB may be needed CC=gcc
MAKE=make AR=ar cr
RM = -rm -rf CFLAGS+=-Wall dirs:=$(shell find . -maxdepth -type d)
dirs:=$(basename $(patsubst ./%,%,$(dirs)))
dirs:=$(filter-out $(exclude_dirs),$(dirs))
SUBDIRS := $(dirs) SRCS=$(wildcard *.c)
OBJS=$(SRCS:%.c=%.o)
DEPENDS=$(SRCS:%.c=%.d) all:$(TARGET) $(LIB) subdirs $(LIB):$(OBJS)
$(AR) $@ $^
cp $@ $(LIBPATH) subdirs:$(SUBDIRS)
for dir in $(SUBDIRS);\
do $(MAKE) -C $$dir all||exit ;\
done $(TARGET):$(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
cp $@ $(EXEPATH) $(OBJS):%.o:%.c
$(CC) -c $< -o $@ $(CFLAGS) -include $(DEPENDS) $(DEPENDS):%.d:%.c
set -e; rm -f $@; \
$(CC) -MM $(CFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[:]*,\1.o $@:,g' < $@.$$$$ > $@; \
rm $@.$$$$ clean:
for dir in $(SUBDIRS);\
do $(MAKE) -C $$dir clean||exit ;\
done
$(RM) $(TARGET) $(LIB) $(OBJS) $(DEPENDS)

当前目录下的子目录是通过shell命令自动得到的,subdirs:$(SUBDIRS) 这块会进入每个子目录执行make,当然有些子目录并不需要编译,可以通过exclude_dirs指定,比如顶层目录的exclude_dirs=bin lib include。

$(DEPENDS):%.d:%.c 这块作用是自动生成头文件依赖,这部分包括5条命令,看起来很复杂,其实原理很简单,假设main.c,包含头文件depend.h, 解析过程如下:

1. @set –e 命令设置当前Shell进程状态为:如果执行的任何一条命令的退出状态非零则立刻终止当前进程。

2. rm -f $@ 删除原来的main.d文件

3. gcc的-MM参数能够生成文件的依赖关系main.o:main.c depend.h,写入文件main.d. $$$$,$$是进程号

4. sed命令作用是将main.o:main.c depend.h替换成main.o main.d:main.c depend.h, 并写入main.d文件

5. rm -f $@.$$$$删除临时文件

Include $(SRCS:.c=.d)将main.d包含进来后,Makefile增加了以下依赖

main.o main.d:main.c depend.h

不管是main.c还是depend.h的变化都会更新main.o 以及main.d,main.d的更新又反过来更新上面这条依赖关系。

这条依赖下面并没有对应的命令,为什么会更新目标文件呢?这跟Makefile的运行步骤有关系,引用下陈浩先生的《跟我一起写Makefile》

GNU的 make 工作时的执行步骤如下:

1、读入所有的 Makefile。

2、读入被 include 的其它 Makefile。

3、初始化文件中的变量。

4、推导隐晦规则,并分析所有规则。

5、为所有的目标文件创建依赖关系链。

6、根据依赖关系,决定哪些目标要重新生成。

7、执行生成命令。

所以1-5 步为第一个阶段,形成了所有的依赖关系链,6-7 为第二个阶段,决定了所有需要生成的目标文件后,执行对应的命令。上面的依赖关系虽然没有命令,但是确定了main.o要重新生成,就会找到以下编译模块生成目标文件

$(OBJS):%.o:%.c
$(CC) -c $< -o $@ $(CFLAGS)

假设有一个模块first,源文件都放在src/first下,Makefile如下

TOPDIR=./../..

LIB=libfirst.a

INCPATH=$(TOPDIR)/include/first
LIBPATH=$(TOPDIR)/lib
CFLAGS= -I$(INCPATH) include $(TOPDIR)/Makefile.env

TOPDIR是相对于顶层目录的相对路径,LIB是要生成的链接库,这样只要几行命令就可以完成当前模块的编译了,而且first下面还可以添加子模块。

假设main.c在src/main目录下,调用了first模块,Makefile如下

TOPDIR=./../..

TARGET=main

LIBPATH=$(TOPDIR)/lib
EXEPATH=$(TOPDIR)/bin CFLAGS= -I$(TOPDIR)/include/first
LDFLAGS= -lfirst include $(TOPDIR)/Makefile.env

TARGET是生成的可执行文件名,在LIBPATH目录下寻找链接库,生成的可执行文件会被mv到EXEPATH目录下

src下没有源文件,只有目录,所以Makefile非常简单

TOPDIR=./..

include $(TOPDIR)/Makefile.env

顶层目录下的Makefile也很简单,相对增加了exclude_dirs,排除不需要编译的目录

TOPDIR=.

exclude_dirs= include  bin  lib

include $(TOPDIR)/Makefile.env

现在只需要在顶层目录下make一下,src下所有目录都会编译,生成的链接库放在lib下,可执行文件在bin目录中。如果要增加新的功能模块,只要在src/目录下新建目录,增加一个类似first下的Makefile即可,是不是很方便?

一个适用于层级目录结构的makefile模版的更多相关文章

  1. 层级目录结构的Makefile递归编译方法

    层级目录结构的Makefile编写方法. 层级目录结构的Makefile编写方法. 0.前言 1.如何编译整个工程 2.过滤每层不需要编译的目录 3将所有输出文件定向输出. 0.前言 假如现在有这样一 ...

  2. Maven 使用了一个标准的目录结构和一个默认的构建生命周期。

    Maven 使用了一个标准的目录结构和一个默认的构建生命周期. 约定优于配置 当创建 Maven 工程时,Maven 会创建默认的工程结构.开发者只需要合理的放置文件,而在 pom.xml 中不再需要 ...

  3. 设计一个树型目录结构的文件系统,其根目录为 root,各分支可以是目录,也可以是文件,最后的叶子都是文件。

    设计一个树型目录结构的文件系统,其根目录为 root,各分支可以是目录,也可以是文件,最后的叶子都是文件. 我实现的功能是提供父目录(兄弟目录),输入文件名,创建树型目录结构,文本文件不可以再有子目录 ...

  4. .net core 第一个mvc项目目录结构简析

    创建项目的命令     首先来认识一下创建项目可使用的各种命令,.NETCore 的命令都以 dotnet 打头,这很好理解,输入 dotnet xxx,就是执行环境变量指向的 C:\Program ...

  5. Eclipse中一个Maven工程的目录结构

    在之前的javaSE开发中,没有很关注Eclipse工程目录下的环境,总是看见一个src就点进去新建一个包再写一个class.以后的日子中也没有机会注意到一个工程到底是怎么组织的这种问题,跟不要说自己 ...

  6. Eclipse中一个Maven工程的目录结构 (MacOS)

    1. 为什么写这篇文章 在之前的javaSE开发中,没有很关注Eclipse工程目录下的环境,总是看见一个src就点进去新建一个包再写一个class.以后的日子中也没有机会注意到一个工程到底是怎么组织 ...

  7. iOS项目的目录结构和开发流程

    转自无网不剩的博客 网上相关的资源不多,开源的且质量还不错的iOS项目也是少之又少,最近正好跟同事合作了一个iOS项目,来说说自己的一些想法.   目录结构 AppDelegate Models Ma ...

  8. VC 2005 解决方案的目录结构设置和管理

    VC 2005 解决方案的目录结构设置和管理   Roger (roger2yi@gmail.com)   一个中等规模的解决方案通常都会包含多个项目,其中一些项目产出静态库,一些产出动态库,一些用于 ...

  9. VS解决方案的目录结构设置和管理

    一个中等规模的解决方案通常都会包含多个项目,其中一些项目产出静态库,一些产出动态库,一些用于单元测试,还有的产出最终的应用程序执行档.除此以外,根据项目的需求,还会使用一些第三方的库.   所以为解决 ...

随机推荐

  1. C语言中的函数

    C语言中的函数 目录 概述——对函数的理解 C语言中函数的定义和声明 函数允许的参数类型 函数允许的返回类型 递归 概述 由于有些代码段在编写程序的时候经常会用到,此时我们为了减少代码文件的长度和增加 ...

  2. DB2 重新设定表自增字段的当前值

    转自:http://blog.csdn.net/jionghan3855/article/details/2709073 1.ALTER TABLE UKEY_INFO_TAB ALTER COLUM ...

  3. RSA非对称算法(转)

    RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它.但是有不少新来的同事对它不太了解,恰好看到一本书中作者用实例对它进行了简化而生动的描述,使得高深的数学理论能够被容易地理解.我们经 ...

  4. 功能分解——Android下画分时图与k线图有感

    最近工作极度繁忙,已经好久没有更新博客了,总感觉要是再不抽空总结总结点东西,分分钟就会被懒惰的状态给打到了.同时也希望同学们谨记,如果你已经决定要坚持某些正确的东西,比如背完某章单词,看一完本书抑或是 ...

  5. Android系统中设置TextView等的行间距

    1.android:lineSpacingExtra 设置行间距,如”2dp”. 2.android:lineSpacingMultiplier 设置行间距的倍数,如”2″.

  6. 【阿里云产品公测】离线归档OAS,在也不用备份担心空间了

    作者:阿里云用户 莫须有3i 1 起步 1.1 初识OAS 啥是OAS,请看官方说明: 引用 开放归档服务(Open Archive Service,简称OAS),致力于提供低成本.高可靠的数据归档服 ...

  7. .net重启iis线程池和iis站点程序代码分享

    重启站点: /// <summary> /// 根据名字重启站点.(没重启线程池) /// </summary> /// <param name="sitena ...

  8. 源自梦想 自定义ViewGroup的整理_2

    Android项目: 1.准备资源图片.图片放到hdip里和mdip里对想过的影响:对于320*480的模拟器,默认去mdip里去找图片资源,拿过来的图片可以直接用,清晰度不变.要是所要找的图片在hd ...

  9. how to build a paper's architecture?

    1. problem? what's the problem? then do some extension of the problem. 2. related works ,which means ...

  10. codeforces 680B B. Bear and Finding Criminals(水题)

    题目链接: B. Bear and Finding Criminals //#include <bits/stdc++.h> #include <vector> #includ ...