Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1415626
  • 博文数量: 241
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2253
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-11 22:27
个人简介

--

文章分类

全部博文(241)

文章存档

2021年(3)

2019年(6)

2018年(1)

2017年(9)

2016年(21)

2015年(50)

2014年(125)

2013年(26)

我的朋友

分类: LINUX

2015-04-04 17:27:09


本文转自:http://blog.csdn.net/zhouyulu/article/details/8395013

本文,主要总结一下小项目中的一种实用makefile写法,为个人项目或小型项目makefile提供参考。
        
        1. 单个源码文件的makefile
        常规的教程,或网上资料,都会以单个源码文件的makefile为示例,讲解makefile的最基本用法。它的用处,除了知道一下all、clean外,无它。
        makefile,主要用于管理实用项目中,较复杂结构的源码编译过程,一般用于c、c++。如果只有一个源码文件,不用makefile,直接gcc/g++更好。有时候,源码要链接特殊的库或包含特殊路径头文件,为了避免忘记,可以使用makefile(另一个建议,是写一个shell脚本,记录gcc/g++的命令)。一个例外是,现在只有一个文件,但后续会扩展成多个文件,应该使用makefile。
        
        2. 一个目录、多个源码文件的makefile
        一个实用的程序,常规包含多个源码文件,此时makefile能派上用场。一个实用makefile如下所示(注意,makefile中,目标的执行语句只能使用tab起始,不能以空格。如all、clean的下一行): 

[plain] view plaincopy
  1. # compile macro  
  2. CC      = g++  
  3. CFLAGS  = -Wall  
  4. LDFLAGS = -lm  
  5. INCLUDES= -I/usr/local/include  
  6.   
  7. # user modify: target and objects  
  8. EXENAME = test  
  9. OBJECTS = x.o y.o z.o  
  10.   
  11. # make target  
  12. all:${OBJECTS}  
  13.     ${CC} -o ${EXENAME} *.o ${LDFLAGS} ${INCLUDES}  
  14.       
  15. # make clean  
  16. clean:  
  17.     rm -rf ${EXENAME} ${OBJECTS}   
  18.   
  19. # dependence      
  20. %.o : %.cpp  
  21.     ${CC} ${CFLAGS} ${INCLUDES} -c $< -o $@  
  22. %.o : %.cc  
  23.     ${CC} ${CFLAGS} ${INCLUDES} -c $< -o $@  

         该makefile的几个说明:
        1)根据项目配置好以下变量:编译器CC,编译选项CFLAGS,链接库LDFLAGS,特殊包含头文件INCLUDES,可执行程序名EXENAME,源码的目标文件OBJECTS。
        2)后续用户新增一个源码文件,只需在OBJECTS中,增加一个对应文件名的目标文件即可搞定(如z.cpp, 增加z.o)。
        3)dependence中,使用了特殊变量的"%","$<","$@",保证了对应源码文件,能够生成匹配的.o文件。

        
        3. 多个目录、多个源码文件的makefile
        实用项目中,多采用分层次目录存放源码。只需在上述makefile中,增加变量VPATH指定子目录,就能搞定。makefile如下所示: 

[plain] view plaincopy
  1. # compile macro  
  2. CC      = g++  
  3. CFLAGS  = -Wall  
  4. LDFLAGS = -lm  
  5. INCLUDES= -I/usr/local/include  
  6.   
  7. # user modify: subdir, target and objects  
  8. VPATH   = subdir1 subdir2  
  9. EXENAME = test  
  10. OBJECTS = x.o y.o z.o  
  11.   
  12. # make target  
  13. all:${OBJECTS}  
  14.     ${CC} -o ${EXENAME} *.o ${LDFLAGS} ${INCLUDES}  
  15.       
  16. # make clean  
  17. clean:  
  18.     rm -rf ${EXENAME} ${OBJECTS}   
  19.   
  20. # dependence      
  21. %.o : %.cpp  
  22.     ${CC} ${CFLAGS} ${INCLUDES} -c $< -o $@  
  23. %.o : %.cc  
  24.     ${CC} ${CFLAGS} ${INCLUDES} -c $< -o $@  

        该makefile的几个说明:
        1)不同目录源码的目标文件,都在OBJECTS中指定;VPATH指定目录。
        2)如果多个目录中,有相同名字的源文件,将按照VPATH的路径先后顺序,只编译第1个。必须调整为不冲突的文件名。(切记)
        3)所有.o文件,都生成到和makefile同一层次的目录中。
        4)项目只有一个makefile,所有目录源码文件的增加删除,都要修改该makefile。(所以推荐在小项目中使用

        
        4. 多个目录拥有独立的makefile
        大中型的实用项目中,多人协作开发,除了分层次目录存放源码,还要考虑源码权限。使用上述的1个makefile,实用性、扩展性,都不能胜任。要使用makefile树管理,每个目录拥有自己的makefile,管控该目录下的编译。
        makefile树内容比较多,将在另一篇文章“项目实用makefile”中说明。



本文转自:http://blog.csdn.net/zhouyulu/article/details/8449263


 在上一篇文章“小项目实用makefile”中,已经说明了单个makefile管理层次目录的局限性。本文,主要总结一下项目中的一种实用makefile树写法,为10来个人协作的中小型项目makefile编写,提供参考。
        
        1. 需求
        从实用角度,makefile树应该达到以下需求:
        1)自动加入编译系统。新增目录、文件后,能够自动添加(理想),或只需少许修改,就能添加到整个项目编译中。
        2)可读性好,易于添加。新增目录、文件,linux新人能自己看懂添加(理想),或只需口头10s描述就能很好完成。
        3)模块化。新增目录、文件后的makefile,和其他目录完全没有关系(理想),或只需与最邻近的前、后有关系。
      

        2. 实现
        2.1 自动加入编译系统
        使用makefile递归执行,能够解决此问题。以下脚本,能够执行SUBDIRS指定的子目录内的makefile。

[plain] view plaincopy
  1. makefile。  
  2. SUBDIRS = modual-a modual-b modual-c  
  3.      
  4. .PHONY: subdirs $(SUBDIRS)  
  5. subdirs: $(SUBDIRS)  
  6. $(SUBDIRS):  
  7.     $(MAKE) -C $@  

        说明:
        1)变量SUBDIRS,指定当前目录下,要递归编译的子目录。
        2).PHONY及subdirs目标结构,能保证递归到子目录中。
            
        2.2 可读性好,易于添加;模块化
        使用makefile的变量定义,其位置前后不敏感特性可以做到。示例脚本如下:

[plain] view plaincopy
  1. SUBDIRS = modual-a modual-b modual-c  
  2. OBJECTS = x.o y.o z.o  
  3.   
  4. all:subdirs ${OBJECTS}  
  5.   
  6. clean:cleansubdirs  
  7.     rm ${OBJECTS}  

        说明:
        1)变量OBJECTS,指定当前目录的目标文件。新增一个文件z.cpp,在变量OBJECTS中,增加z.o即可。
        2)新增一个目录dir-c,在变量SUBDIRS中,增加dir-c。然后参考本目录中的makfile,在dir-c中,建立一个类似makfile即可。
        
        2.3 减少重复脚本
        实现中,将makefile中的共用变量,转移到文件中,使用include包含,能够达到函数效果,减少重复脚本,容易改变。

        
        
3. 实例
        示例项目目录如下所示()

[plain] view plaincopy
  1. project-test  
  2.   +-- makeconfig  
  3.   |     +-- make.global  
  4.   +-- src  
  5.   |     +-- module-a  
  6.   |     |     +-- test.cpp  
  7.   |     |     +-- Makefile  
  8.   |     +-- module-b  
  9.   |     |     +-- test.cpp  
  10.   |     |     +-- Makefile  
  11.   |     +-- main.cpp  
  12.   |     +-- Makefile  
  13.   +-- Makefile  

        说明:
        1)project-test/makeconfig/make.global,要包含的makefile共用变量。
        2)project-test/Makefile,顶层makefile,指定可执行目标,及源码目录。
        3)project-test/src/Makefile,子目录的makeflile。目录module-a、module-b的类似,每个目录一个。
        
        3.1 project-test/makeconfig/make.global
        如下所示:

[plain] view plaincopy
  1. # compile macro  
  2. CC      = g++  
  3. CFLAGS      = -O2 -Wall  
  4. LDFLAGS = -lm   
  5. INCLUDES    = -I/usr/local/include  
  6.   
  7.   
  8. # recursive make  
  9. .PHONY: subdirs ${SUBDIRS} cleansubdirs  
  10. subdirs: ${SUBDIRS}  
  11. ${SUBDIRS}:  
  12.     ${MAKE} -C $@ all  
  13.   
  14.       
  15. # recursive make clean  
  16. cleansubdirs:  
  17.     @for dir in ${SUBDIRS}; do \  
  18.         ${MAKE} -C $$dir clean; \  
  19.     done  
  20.   
  21.       
  22. # dependence  
  23. %.o: %.cpp  
  24.     ${CC} ${CFLAGS} ${INCLUDES} -c $< -o $@  
  25. %.o: %.cc  
  26.     ${CC} ${CFLAGS} ${INCLUDES} -c $< -o $@    

        说明:
        1)包含4个区域,共用变量,递归make,递归makeclean,依赖关系。
        2)递归makeclean,使用了不打印的shell语法。原因是,如果和递归一样写,会造成目标重载警告。
        
        3.2 project-test/Makefile
        如下所示: 

[plain] view plaincopy
  1. # target, subdir, objects in current dir  
  2. TARGET      = test  
  3. SUBDIRS = src  
  4. OBJECTS =   
  5.   
  6.   
  7. all:subdirs ${OBJECTS}  
  8.     ${CC} -o ${TARGET} $$(find ./${SUBDIRS} -name '*.o') ${LDFLAGS} ${INCLUDES}  
  9.   
  10.   
  11. clean:cleansubdirs  
  12.     rm -f ${TARGET} ${OBJECTS}  
  13.   
  14.   
  15. # path of "make global scripts"  
  16. # NOTE, use absolute path. export once, use in all subdirs  
  17. export PROJECTPATH=${PWD}  
  18. export MAKEINCLUDE=${PROJECTPATH}/makeconfig/make.global  
  19.   
  20. # include "make global scripts"  
  21. include ${MAKEINCLUDE}  

        说明:
        1)使用shell语法,搜索出指定目录的所有.o,链接为执行目标文件。
        2)使用export,指定项目绝对路径,指定共用变量,包含所有目录共用的makefile变量。
        3)每个目标使用一个顶层的makefile,来执行make和makeclean的递归入口,共用makefile。
        
        3.3 project-test/src/Makefile
        如下所示:

[plain] view plaincopy
  1. # subdir and objects in current dir  
  2. SUBDIRS = module-a module-b  
  3. OBJECTS = main.o  
  4.   
  5.   
  6. all:subdirs ${OBJECTS}  
  7.   
  8.       
  9. clean:cleansubdirs  
  10.     rm -f ${OBJECTS}  
  11.       
  12. include ${MAKEINCLUDE}  

        说明:
        1)增加目录,只用修改子目录变量SUBDIRS;增加文件,修改当前目标文件变量OBJECTS。
        2)不同目录下,源码相同名字,但类、函数不相同,可以正常编译。        
        
        
4. 专业makefile树
        以上,只是一个项目最最普通的makefile树。一个实现文件一个.o文件,不考虑库生成,不考虑功能配置项,不考虑平台兼容性。
        一些开源项目,考虑了各种平台兼容性,及功能特性,通常使用了autoconf和automake,自动生成特殊头文件和宏定义,来达到效果。使用以下3条命令,向用户提供配置项设置,编译,库、头文件、或目标文件安装路径。在复杂兼容项目中,非常实用。
        ./configure
        make
        make install
        还在继续学习中。
        
        参考资料:
        1. GNU Make Manual:

        2. 同事J的makefile树。

阅读(1733) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~