Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3105583
  • 博文数量: 396
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4209
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-04 13:04
文章分类

全部博文(396)

文章存档

2022年(1)

2021年(2)

2020年(8)

2019年(24)

2018年(135)

2017年(158)

2016年(68)

我的朋友

分类: LINUX

2016-08-31 15:27:48

版权声明:转载请保留出处:blog.csdn.net/gentleliu。Mail:shallnew at 163 dot com】

在大一些的项目里面,所有源代码不会只放在同一个目录,一般各个功能模块的源代码都是分开的,各自放在各自目录下,并且头文件和.c源文件也会有各 自的目录,这样便于项目代码的维护。这样我们可以在每个功能模块目录下都写一个Makefile,各自Makefile处理各自功能的编译链接工作,这样 我们就不必把所有功能的编译链接都放在同一个Makefile里面,这可使得我们的Makefile变得更加简洁,并且编译的时候可选择编译哪一个模块, 这对分块编译有很大的好处。

现在我所处于工程目录树如下:

[plain] view plain copy
  1. .  
  2.   
  3. ├── include  
  4. │   ├── common.h  
  5. │   ├── ipc  
  6. │   │   └── ipc.h  
  7. │   └── tools  
  8. │       ├── base64.h  
  9. │       ├── md5.h  
  10. │       └── tools.h  
  11. ├── Makefile  
  12. ├── src  
  13. │   ├── ipc  
  14. │   │   ├── inc  
  15. │   │   ├── Makefile  
  16. │   │   └── src  
  17. │   │       └── ipc.c  
  18. │   ├── main  
  19. │   │   ├── inc  
  20. │   │   ├── Makefile  
  21. │   │   └── src  
  22. │   │       ├── main.c  
  23. │   │       └── main.c~  
  24. │   └── tools  
  25. │       ├── inc  
  26. │       ├── Makefile  
  27. │       └── src  
  28. │           ├── base64.c  
  29. │           ├── md5.c  
  30. │           └── tools.c  
  31. └── tags  
  32.   
  33. 13 directories, 16 files  

这样组织项目源码要比之前合理一些,那这样怎么来写Makefile呢?我们可以在每个目录下写一个Makefile,通过最顶层的Makefile一层一层的向下嵌套执行各层Makefile。那么我们最顶层的Makefile简单点的话可以这样写:

[plain] view plain copy
  1. # top Makefile for xxx  
  2.   
  3. all :  
  4. >---$(MAKE) -C src  
  5.   
  6. tags:  
  7. >---ctags -R  
  8.   
  9. clean :  
  10. >---$(MAKE) -C src clean  
  11.   
  12. .PHONY : all clean tags  

命令:

>---$(MAKE) -C src

就是进入src目录继续执行该目录下的Makefile。然后src目录下的Makefile在使用同样的方法进入下一级目录tools、 main、ipc,再执行该目录下的Makefile。其实这样有些麻烦,我们可以直接从顶层目录进入最后的目录执行make。再加入一些伪目标完善下, 我们的顶层Makefile就出来了:

[plain] view plain copy
  1. # Top Makefile for C program  
  2.   
  3. # Copyright (C) 2014 shallnew \at 163 \dot com  
  4.   
  5. all :  
  6. >---$(MAKE) -C src/ipc  
  7. >---$(MAKE) -C src/tools  
  8. >---$(MAKE) -C src/main  
  9.   
  10. tags:  
  11. >---ctags -R  
  12.   
  13. help:  
  14. >---@echo "===============A common Makefilefor c programs=============="  
  15. >---@echo "Copyright (C) 2014 liuy0711 \at 163\dot com"  
  16. >---@echo "The following targets aresupport:"  
  17. >---@echo  
  18. >---@echo " all              - (==make) compile and link"  
  19. >---@echo " obj              - just compile, withoutlink"  
  20. >---@echo " clean            - clean target"  
  21. >---@echo " distclean        - clean target and otherinformation"  
  22. >---@echo " tags             - create ctags for vimeditor"  
  23. >---@echo " help             - print help information"  
  24. >---@echo  
  25. >---@echo "To make a target, do 'make[target]'"  
  26. >---@echo "========================= Version2.0 ======================="  
  27.   
  28. obj:  
  29. >---$(MAKE) -C src/ipc obj  
  30. >---$(MAKE) -C src/tools obj  
  31. >---$(MAKE) -C src/main obj  
  32.   
  33. clean :  
  34. >---$(MAKE) -C src/ipc clean  
  35. >---$(MAKE) -C src/tools clean  
  36. >---$(MAKE) -C src/main clean  
  37.   
  38. distclean:  
  39. >---$(MAKE) -C src/ipc distclean  
  40. >---$(MAKE) -C src/tools distclean  
  41. >---$(MAKE) -C src/main distclean  
  42.   
  43. .PHONY : all clean distclean tags help  
当 我们这样组织源代码时,最下面层次的Makefile怎么写呢?肯定不可以将我们上一节的Makefile(version 1.1)直接拷贝到功能模块目录下,需要稍作修改。不能所有的模块都最终生成各自的可执行文件吧,我们目前是一个工程,所以最后只会生成一个可执行程序。 我们这样做,让主模块目录生成可执行文件,其他模块目录生成静态库文件,主模块链接时要用其他模块编译产生的库文件来生成最终的程序。将上一节 Makefile稍作修改得出编译库文件Makefile和编译可执行文件Makefile分别如下:
[plain] view plain copy
  1. # A Makefile to generate archive file  
  2. # Copyright (C) 2014 shallnew \at 163 \dot com  
  3.   
  4.   
  5. CFLAGS += -g -Wall -Werror -O2  
  6. CPPFLAGS += -I. -I./inc -I../../include  
  7.   
  8. # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))  
  9. SRC_FILES = $(wildcard src/*.c)  
  10. SRC_OBJ = $(SRC_FILES:.c=.o)  
  11. SRC_LIB = libtools.a  
  12.   
  13. all : $(SRC_LIB)  
  14.   
  15. $(SRC_LIB) : $(SRC_OBJ)  
  16. >---$(AR) rcs $@ $^  
  17. >---cp $@ ../../libs  
  18.   
  19. obj : $(SRC_OBJ)  
  20.   
  21. # clean target  
  22. clean:  
  23. >---$(RM) $(SRC_OBJ) $(SRC_LIB)  
  24.   
  25. distclean:  
  26. >---$(RM) $(SRC_OBJ) $(SRC_LIB) tags *~  
  27.   
  28. .PHONY : all obj clean disclean  
==========================================================================
[plain] view plain copy
  1. # A Makefile to generate executive file                                                                                                                                                     
  2. # Copyright (C) 2014 shallnew \at 163 \dot com  
  3.   
  4. CFLAGS += -g -Wall -Werror -O2  
  5. CPPFLAGS += -I. -I./inc -I../../include  
  6. LDFLAGS += -lpthread -L../../libs -ltools -lipc  
  7.   
  8.   
  9. # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))  
  10. SRC_FILES = $(wildcard src/*.c)  
  11. SRC_OBJ = $(SRC_FILES:.c=.o)    
  12. SRC_BIN = target_bin            
  13.   
  14. all : $(SRC_BIN)  
  15.   
  16. $(SRC_BIN) : $(SRC_OBJ)         
  17. >---$(CC) -o $@ $^ $(LDFLAGS)   
  18.   
  19. obj : $(SRC_OBJ)  
  20.   
  21. # clean target  
  22. clean:  
  23. >---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe  
  24.   
  25. distclean:  
  26. >---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe tags*~  
  27.   
  28. .PHONY : all obj clean disclean  

最后在顶层执行:

[plain] view plain copy
  1. # make clean  
  2.   
  3. make -C src/ipc clean  
  4. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'  
  5. rm -f src/ipc.o libipc.a  
  6. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/ipc'  
  7. make -C src/tools clean  
  8. make[1]: Entering directory `/home/Myprojects/example_make/version-3.0/src/tools'  
  9. rm -f src/base64.o src/md5.o src/tools.o libtools.a  
  10. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'  
  11. make -C src/main clean  
  12. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'  
  13. rm -f src/main.o target_bin target_bin.exe  
  14. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'  
  15. # make  
  16. make -C src/ipc  
  17. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'  
  18. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/ipc.osrc/ipc.c  
  19. ar rcs libipc.a src/ipc.o  
  20. cp libipc.a ../../libs  
  21. make[1]: Leaving directory `/home/Myprojects/example_make/version-3.0/src/ipc'  
  22. make -C src/tools  
  23. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/tools'  
  24. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/base64.osrc/base64.c  
  25. cc -g -Wall -Werror -O2 -I. -I./inc -I../../include  -c -o src/md5.o src/md5.c  
  26. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/tools.osrc/tools.c  
  27. ar rcs libtools.a src/base64.o src/md5.o src/tools.o  
  28. cp libtools.a ../../libs  
  29. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'  
  30. make -C src/main  
  31. make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'  
  32. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/main.osrc/main.c  
  33. cc -o target_bin src/main.o -lpthread -L../../libs -ltools-lipc  
  34. make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'  
  35. #  

最后生成了可执行程序文件。这样的话一个工程的各个模块就变得独立出来了,不但源码分开了,而且各自有各自的Makefile,并且各个功能模块是可独立编译的。

我们发现顶层Makefile还有可以改进的地方,就是在进入下一层目录是要重复写多次,如下:

[plain] view plain copy
  1. >---$(MAKE) -C src/ipc  
  2. >---$(MAKE) -C src/tools  
  3. >---$(MAKE) -C src/main  

每增加一个目录都要在多个伪目标里面加入一行,这样不够自动化啊,于是我们想到shell的循环语 句,我们可以在每条规则的命令处使用for循环。如下:

[plain] view plain copy
  1. DIR = src  
  2. SUBDIRS = $(shell ls $(DIR))  
  3.   
  4. all :  
  5. >---@for subdir in $(SUBDIRS); \  
  6. >---do $(MAKE) -C $(DIR)/$$subdir; \                                                                                                                                             
  7. >---done  

这样懒人有可以高兴很久了。不过还有问题:

上面for循环会依次进入系统命令ls列出的目录,但我们对每个目录的make顺序可能有要求,在该项目当中,main目录下的Makefile必 须最后执行,因为最终的链接需要其他目录编译生成的库文件,否则会执行失败。并且在当前的Makefile中,当子目录执行make出现错误时,make 不会退出。在最终执行失败的情况下,我们很难根据错误的提示定位出具体是是那个目录下的Makefile出现错误。这给问题定位造成了很大的困难。为了避 免这样的问题,在命令执行错误后make退出。

所以将刚才的Makefile修改为如下

 

[plain] view plain copy
  1. DIR = src  
  2. SUBDIRS = $(shell ls $(DIR))  
  3.    
  4. all :  
  5. >---@for subdir in $(SUBDIRS); \  
  6. >---do $(MAKE) -C $(DIR)/$$subdir || exit 1; \                                                                                                                                             
  7. >---done  

这样在执行出错时立马退出,但这样还是没有解决问题,编译错误还是会出现。那怎么解决呢?

我们可以通过增加规则来限制make执行顺序,这样就要用到伪目标,对每一个模块我们都为他写一条规则,每个模块名称是目标,最后需要执行的模块目 标又是其他模块的目标,这样就限制了make顺序。在执行到最后需要执行的目标时,发现存在依赖,于是先更新依赖的目标,这样就不会出错了。并且这样的 话,我们还可以对指定模块进行编译,比如我只修改了tools模块,我只想看看我修改的这个模块代码是否可以编译通过,我可以在编译时这样:

[plain] view plain copy
  1. # make tools  
  2. make -C src/tools  
  3. make[1]: Entering directory`/home/Myprojects/example_make/version-2.1/src/tools'  
  4. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/base64.o src/base64.c  
  5. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/md5.osrc/md5.c  
  6. cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/tools.osrc/tools.c  
  7. ar rcs libtools.a src/base64.o src/md5.o src/tools.o  
  8. cp libtools.a ../../libs  
  9. make[1]: Leaving directory`/home/Myprojects/example_make/version-2.1/src/tools'  
  10. #  
还 有另外一种方法也可以解决此问题,就是手动列出需要进入执行的模块名称(这里就是目录了),把最后需要执行的模块放在最后,这样for循环执行时最后需要 编译链接的模块就放在最后了,不会像我们之前那样make是按照使用系统命令ls列出模块目录的顺序来执行。ls列出目录是按照每个目录的名称来排序的, 我们总不能要求写代码的时候最后执行的模块的名称必须是以z开头的吧,总之不现实。

 我们的顶层Makefile又进化了,也是这一节最终Makefile:

[plain] view plain copy
  1. # Top Makefile for C program  
  2. # Copyright (C) 2014 shallnew \at 163 \dot com  
  3.   
  4. DIR = src  
  5. MODULES = $(shell ls $(DIR))  
  6. # MODULES = ipc main tools  
  7.   
  8. all : $(MODULES)  
  9.    
  10. $(MODULES):  
  11. >---$(MAKE) -C $(DIR)/$@  
  12.    
  13. main:tools ipc  
  14.    
  15. obj:  
  16. >---@for subdir in $(MODULES); \  
  17. >---do $(MAKE) -C $(DIR)/$$subdir $@; \  
  18. >---done  
  19.    
  20. clean :  
  21. >---@for subdir in $(MODULES); \  
  22. >---do $(MAKE) -C $(DIR)/$$subdir $@; \  
  23. >---done  
  24.    
  25. distclean:  
  26. >---@for subdir in $(MODULES); \  
  27. >---do $(MAKE) -C $(DIR)/$$subdir $@; \  
  28. >---done  
  29.    
  30.    
  31. tags:  
  32. >---ctags -R  
  33.   
  34. help:  
  35. >---@echo "===============A common Makefilefor c programs=============="  
  36. >---@echo "Copyright (C) 2014 liuy0711 \at 163\dot com"  
  37. >---@echo "The following targets aresupport:"  
  38. >---@echo  
  39. >---@echo " all              - (==make) compile and link"  
  40. >---@echo " obj              - just compile, withoutlink"  
  41. >---@echo " clean            - clean target"  
  42. >---@echo " distclean        - clean target and otherinformation"  
  43. >---@echo " tags             - create ctags for vimeditor"  
  44. >---@echo " help             - print help information"  
  45. >---@echo  
  46. >---@echo "To make a target, do 'make[target]'"  
  47. >---@echo "========================= Version2.0 ======================="  
  48.    
  49. .PHONY : all clean distclean tags help
阅读(2252) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~