今天写了个层次化的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. SQL Server 2008 清空删除日志文件

    USE [master]GOALTER DATABASE STAR9SQL SET RECOVERY SIMPLE WITH NO_WAITGOALTER DATABASE STAR9SQL SET ...

  2. 解决win7 64位中 魔方与TortoiseSVN的冲突解决【2014-02-10】

    原文地址:http://www.cnblogs.com/hbbbs/p/3542479.html 现象 启动后弹出SendRpt:Error的提示框,然后变成soap1.2 fault.关闭后,又会自 ...

  3. CENTOS YUM软件源

    CentOS 7.0 使用 YUM 安装 MySQL 报错 问题现象 CentOS 7.0 使用 YUM 安装 MySQL 时出现类似如下错误信息: File contains no section ...

  4. Servlet 3.0 新特性

    Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布.该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发 ...

  5. GNU-ARM汇编

    GNU ARM 汇编指令(2008-10-29 00:16:10) 标签:linux gnu arm 汇编指令 it 分类:技术文摘 第一部分 Linux下ARM汇编语法尽管在Linux下使用C或C+ ...

  6. B - 娜娜梦游仙境系列——跳远女王

    B - 娜娜梦游仙境系列——跳远女王 Time Limit: 2000/1000MS (Java/Others)    Memory Limit: 128000/64000KB (Java/Other ...

  7. IOS 读取本地的Json/plist 文件

    一.一般本地可以存储轻量级数据存储 plist  这个主要是操作字典 方法如下: NSString * sampleFile= [[[NSBundle mainBundle] bundlePath] ...

  8. RegisterStartupScript和RegisterClientScriptBlock的用法

    RegisterStartupScript和RegisterClientScriptBlock的用法 RegisterStartupScript(key, script) RegisterClient ...

  9. centos(linux) 下如何查看端口占用情况及杀死进程

    使用这个命令:netstat -nap [root@Jaosn sphinx]# netstat -nap Active Internet connections (servers and estab ...

  10. Spring学习总结二——SpringIOC容器二

    一:指定bean的依赖关系 例如examplebean对象依赖examplebean1对象,那么在创建examplebean对象之前就 需要先创建examplebean1对象. 1:创建Example ...