zoukankan      html  css  js  c++  java
  • 从头開始写项目Makefile(五):嵌套运行

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

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

    如今我所处于project文件夹树例如以下:

    .
    
    ├── include
    │   ├── common.h
    │   ├── ipc
    │   │   └── ipc.h
    │   └── tools
    │       ├── base64.h
    │       ├── md5.h
    │       └── tools.h
    ├── Makefile
    ├── src
    │   ├── ipc
    │   │   ├── inc
    │   │   ├── Makefile
    │   │   └── src
    │   │       └── ipc.c
    │   ├── main
    │   │   ├── inc
    │   │   ├── Makefile
    │   │   └── src
    │   │       ├── main.c
    │   │       └── main.c~
    │   └── tools
    │       ├── inc
    │       ├── Makefile
    │       └── src
    │           ├── base64.c
    │           ├── md5.c
    │           └── tools.c
    └── tags
    
    13 directories, 16 files
    

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

    # top Makefile for xxx
    
    all :
    >---$(MAKE) -C src
    
    tags:
    >---ctags -R
    
    clean :
    >---$(MAKE) -C src clean
    
    .PHONY : all clean tags
    
    
    

    命令:

    >---$(MAKE) -C src

    就是进入src文件夹继续运行该文件夹下的Makefile。然后src文件夹下的Makefile在使用相同的方法进入下一级文件夹tools、main、ipc。再运行该文件夹下的Makefile。事实上这样有些麻烦。我们能够直接从顶层文件夹进入最后的文件夹运行make。再增加一些伪目标完好下。我们的顶层Makefile就出来了:

    # Top Makefile for C program
    
    # Copyright (C) 2014 shallnew at 163 dot com
    
    all :
    >---$(MAKE) -C src/ipc
    >---$(MAKE) -C src/tools
    >---$(MAKE) -C src/main
    
    tags:
    >---ctags -R
    
    help:
    >---@echo "===============A common Makefilefor c programs=============="
    >---@echo "Copyright (C) 2014 liuy0711 at 163dot com"
    >---@echo "The following targets aresupport:"
    >---@echo
    >---@echo " all              - (==make) compile and link"
    >---@echo " obj              - just compile, withoutlink"
    >---@echo " clean            - clean target"
    >---@echo " distclean        - clean target and otherinformation"
    >---@echo " tags             - create ctags for vimeditor"
    >---@echo " help             - print help information"
    >---@echo
    >---@echo "To make a target, do 'make[target]'"
    >---@echo "========================= Version2.0 ======================="
    
    obj:
    >---$(MAKE) -C src/ipc obj
    >---$(MAKE) -C src/tools obj
    >---$(MAKE) -C src/main obj
    
    clean :
    >---$(MAKE) -C src/ipc clean
    >---$(MAKE) -C src/tools clean
    >---$(MAKE) -C src/main clean
    
    distclean:
    >---$(MAKE) -C src/ipc distclean
    >---$(MAKE) -C src/tools distclean
    >---$(MAKE) -C src/main distclean
    
    .PHONY : all clean distclean tags help
    
    当我们这样组织源码时。最以下层次的Makefile怎么写呢?肯定不能够将我们上一节的Makefile(version 1.1)直接复制到功能模块文件夹下,须要稍作改动。

    不能全部的模块都终于生成各自的可运行文件吧,我们眼下是一个project,所以最后仅仅会生成一个可运行程序。我们这样做,让主模块文件夹生成可运行文件。其它模块文件夹生成静态库文件,主模块链接时要用其它模块编译产生的库文件来生成终于的程序。将上一节Makefile稍作改动得出编译库文件Makefile和编译可运行文件Makefile分别例如以下:

    # A Makefile to generate archive file
    # Copyright (C) 2014 shallnew at 163 dot com
    
    
    CFLAGS += -g -Wall -Werror -O2
    CPPFLAGS += -I. -I./inc -I../../include
    
    # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
    SRC_FILES = $(wildcard src/*.c)
    SRC_OBJ = $(SRC_FILES:.c=.o)
    SRC_LIB = libtools.a
    
    all : $(SRC_LIB)
    
    $(SRC_LIB) : $(SRC_OBJ)
    >---$(AR) rcs $@ $^
    >---cp $@ ../../libs
    
    obj : $(SRC_OBJ)
    
    # clean target
    clean:
    >---$(RM) $(SRC_OBJ) $(SRC_LIB)
    
    distclean:
    >---$(RM) $(SRC_OBJ) $(SRC_LIB) tags *~
    
    .PHONY : all obj clean disclean
    
    ==========================================================================

    # A Makefile to generate executive file                                                                                                                                                   
    # Copyright (C) 2014 shallnew at 163 dot com
    
    CFLAGS += -g -Wall -Werror -O2
    CPPFLAGS += -I. -I./inc -I../../include
    LDFLAGS += -lpthread -L../../libs -ltools -lipc
    
    
    # SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
    SRC_FILES = $(wildcard src/*.c)
    SRC_OBJ = $(SRC_FILES:.c=.o)  
    SRC_BIN = target_bin          
    
    all : $(SRC_BIN)
    
    $(SRC_BIN) : $(SRC_OBJ)       
    >---$(CC) -o $@ $^ $(LDFLAGS) 
    
    obj : $(SRC_OBJ)
    
    # clean target
    clean:
    >---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe
    
    distclean:
    >---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe tags*~
    
    .PHONY : all obj clean disclean
    

    最后在顶层运行:

    # make clean
    
    make -C src/ipc clean
    make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
    rm -f src/ipc.o libipc.a
    make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/ipc'
    make -C src/tools clean
    make[1]: Entering directory `/home/Myprojects/example_make/version-3.0/src/tools'
    rm -f src/base64.o src/md5.o src/tools.o libtools.a
    make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
    make -C src/main clean
    make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
    rm -f src/main.o target_bin target_bin.exe
    make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
    # make
    make -C src/ipc
    make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
    cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/ipc.osrc/ipc.c
    ar rcs libipc.a src/ipc.o
    cp libipc.a ../../libs
    make[1]: Leaving directory `/home/Myprojects/example_make/version-3.0/src/ipc'
    make -C src/tools
    make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/tools'
    cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/base64.osrc/base64.c
    cc -g -Wall -Werror -O2 -I. -I./inc -I../../include  -c -o src/md5.o src/md5.c
    cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/tools.osrc/tools.c
    ar rcs libtools.a src/base64.o src/md5.o src/tools.o
    cp libtools.a ../../libs
    make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
    make -C src/main
    make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
    cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/main.osrc/main.c
    cc -o target_bin src/main.o -lpthread -L../../libs -ltools-lipc
    make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
    #
    

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

    我们发现顶层Makefile还有能够改进的地方,就是在进入下一层文件夹是要反复写多次,例如以下:

    >---$(MAKE) -C src/ipc
    >---$(MAKE) -C src/tools
    >---$(MAKE) -C src/main
    

    每添加一个文件夹都要在多个伪目标里面添加一行。这样不够自己主动化啊,于是我们想到shell的循环语 句,我们能够在每条规则的命令处使用for循环。

    例如以下:

    DIR = src
    SUBDIRS = $(shell ls $(DIR))
    
    all :
    >---@for subdir in $(SUBDIRS); 
    >---do $(MAKE) -C $(DIR)/$$subdir;                                                                                                                                            
    >---done

    这样懒人有能够高兴非常久了。

    只是还有问题:

    上面for循环会依次进入系统命令ls列出的文件夹,但我们对每一个文件夹的make顺序可能有要求,在该项目其中。main文件夹下的Makefile必须最后运行,由于终于的链接须要其它文件夹编译生成的库文件,否则会运行失败。

    而且在当前的Makefile中,当子文件夹运行make出现错误时。make不会退出。在终于运行失败的情况下,我们非常难依据错误的提示定位出详细是是那个文件夹下的Makefile出现错误。这给问题定位造成了非常大的困难。为了避免这种问题,在命令运行错误后make退出。

    所以将刚才的Makefile改动为例如以下

    DIR = src
    SUBDIRS = $(shell ls $(DIR))
     
    all :
    >---@for subdir in $(SUBDIRS); 
    >---do $(MAKE) -C $(DIR)/$$subdir || exit 1;                                                                                                                                            
    >---done
    

    这样在运行出错时立刻退出,但这样还是没有解决这个问题。编译错误还是会出现。

    那怎么解决呢?

    我们能够通过添加规则来限制make运行顺序,这样就要用到伪目标,对每个模块我们都为他写一条规则,每个模块名称是目标,最后须要运行的模块目标又是其它模块的目标,这样就限制了make顺序。在运行到最后须要运行的目标时,发现存在依赖,于是先更新依赖的目标,这样就不会出错了。而且这种话,我们还能够对指定模块进行编译,比方我仅仅改动了tools模块,我仅仅想看看我改动的这个模块代码能否够编译通过,我能够在编译时这样:

    # make tools
    make -C src/tools
    make[1]: Entering directory`/home/Myprojects/example_make/version-2.1/src/tools'
    cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/base64.o src/base64.c
    cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/md5.osrc/md5.c
    cc -g -Wall -Werror -O2 -I. -I./inc-I../../include  -c -o src/tools.osrc/tools.c
    ar rcs libtools.a src/base64.o src/md5.o src/tools.o
    cp libtools.a ../../libs
    make[1]: Leaving directory`/home/Myprojects/example_make/version-2.1/src/tools'
    #
    还有第二种方法也能够解决此问题,就是手动列出须要进入运行的模块名称(这里就是文件夹了)。把最后须要运行的模块放在最后,这样for循环运行时最后须要编译链接的模块就放在最后了,不会像我们之前那样make是依照使用系统命令ls列出模块文件夹的顺序来运行。

    ls列出文件夹是依照每一个文件夹的名称来排序的,我们总不能要求写代码的时候最后运行的模块的名称必须是以z开头的吧。总之不现实。

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

    # Top Makefile for C program
    # Copyright (C) 2014 shallnew at 163 dot com
    
    DIR = src
    MODULES = $(shell ls $(DIR))
    # MODULES = ipc main tools
    
    all : $(MODULES)
     
    $(MODULES):
    >---$(MAKE) -C $(DIR)/$@
     
    main:tools ipc
     
    obj:
    >---@for subdir in $(MODULES); 
    >---do $(MAKE) -C $(DIR)/$$subdir $@; 
    >---done
     
    clean :
    >---@for subdir in $(MODULES); 
    >---do $(MAKE) -C $(DIR)/$$subdir $@; 
    >---done
     
    distclean:
    >---@for subdir in $(MODULES); 
    >---do $(MAKE) -C $(DIR)/$$subdir $@; 
    >---done
     
     
    tags:
    >---ctags -R
    
    help:
    >---@echo "===============A common Makefilefor c programs=============="
    >---@echo "Copyright (C) 2014 liuy0711 at 163dot com"
    >---@echo "The following targets aresupport:"
    >---@echo
    >---@echo " all              - (==make) compile and link"
    >---@echo " obj              - just compile, withoutlink"
    >---@echo " clean            - clean target"
    >---@echo " distclean        - clean target and otherinformation"
    >---@echo " tags             - create ctags for vimeditor"
    >---@echo " help             - print help information"
    >---@echo
    >---@echo "To make a target, do 'make[target]'"
    >---@echo "========================= Version2.0 ======================="
     
    .PHONY : all clean distclean tags help
  • 相关阅读:
    导出EXCEL 没有乱码,已确认可以使用, 转自csdn
    发送微信模板消息 已测试通过
    mysql 无法启动 innodb 报错 1067
    redis3.0集群搭建
    centos下编译openjdk1.8
    Redis无法远程访问
    记一次优化的的意外收获
    mysql的基础优化
    关于inputStream.read()返回值的问题
    rest风格的url接收
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/6789526.html
Copyright © 2011-2022 走看看