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
  • 相关阅读:
    python模块--time模块
    python模块--如何相互调用自己写的模块
    Animating Views Using Scenes and Transitions
    fragment 切换
    android textview 设置text 字体
    android intent 5.1
    android EditView ime
    animation of android (4)
    animation of android (3)
    animation of android (2)
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/6789526.html
Copyright © 2011-2022 走看看