今天写了个层次化的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. oc-24-点语法

    /** 点语法的本质是方法的调用,而不是访问成员变量,当使用点语法时, 编译器会自动展开成相应的方法.切记点语法的本质是转换成相应的set和get方法, 如果没有set和get方法,则不能使用点语法. ...

  2. Systemd 入门教程:实战篇

    Systemd 入门教程:实战篇 上一篇文章,介绍了 Systemd 的主要命令,这篇文章主要介绍如何使用 Systemd 来管理我们的服务,以及各项的含义: 一.开机启动 对于那些支持 System ...

  3. 构建高性能服务(三)Java高性能缓冲设计 vs Disruptor vs LinkedBlockingQueue--转载

    原文地址:http://maoyidao.iteye.com/blog/1663193 一个仅仅部署在4台服务器上的服务,每秒向Database写入数据超过100万行数据,每分钟产生超过1G的数据.而 ...

  4. Java 7之集合类型 - 二叉排序树、平衡树、红黑树---转

    http://blog.csdn.net/mazhimazh/article/details/19961017 为了理解 TreeMap 的底层实现,必须先介绍排序二叉树和平衡二叉树,然后继续介绍红黑 ...

  5. 关于Android WindowManager显示悬浮窗的动画效果

    要实现WindowManager添加的窗口,实现动画显示,就需要添加如下红色的属性,其他的添加View只要设置其Animations属性也会实现动画,当然自己实现也可,但是能直接用系统的已经实现好的, ...

  6. python UTC,时间戳,字符串转换

    #!/usr/bin/env python #_*_coding:utf-8_*_ # 本地时间 转换 为时间戳 import time import pytz import datetime dat ...

  7. 二手奢侈品电商Vestiaire Collective融资2000万美元

    巴黎奢侈品电商Vestiaire Collective获得了2000万美元的C轮融资,投资方包括知名出版集团Condé Nast.Idinvest.Balderton和Ventech,其中Condé ...

  8. IOS 给图片添加水印(文字)

    有时候上传图片要加唯一标识,简单的就是添加一个水印.这里水印我们讲文字,可以是当前系统时间.坐标.地理位置等 原理就是把一个字符串写到图片上,并且字(font)的大小由图片大小控制. 以下是封装好的一 ...

  9. DataGridView 多线程更新 数据 解决卡顿问题

    使用多线程更新DataGridView,防止页面卡顿和卡死的问题 private delegate void UpdateDataGridView(DataTable dt); private voi ...

  10. [JAVA] HTTP请求,返回响应内容,实例及应用

    JDK 中提供了一些对无状态协议请求(HTTP )的支持,下面我就将我所写的一个小例子(组件)进行描述: 首先让我们先构建一个请求类(HttpRequester ). 该类封装了 JAVA 实现简单请 ...