zoukankan      html  css  js  c++  java
  • Makefile学习10————为依赖关系文件建立依赖关系

    现在我们再对complicated项目做一些更改,增加程序文件间依赖关系的复杂度。

    /×    main.c   ×/
    #include"foo.h"
    int main(void)
    {
        foo();
        return 0;
    }
    
    /*    foo.c    */
    #include<stdio.h>
    #include"foo.h"
    void foo(void)
    {
        printf("%s,This is foo()!
    ",HELLO);
    }
    
    
    /*   foo.h     */
    #ifndef __FOO_H
    #define __FOO_H
    #include "define.h"
    void foo(void);
    
    #endif /*__FOO_H*/
    
    /*    define.h    */
    #ifndef __DEFINE_H
    #define __DEFINE_H
    
    #define HELLO "hello"
    
    #endif/*__DEFINE_H*/

    在之前的Makefile不做更改的情况下,我们make一下:

     在这次成功编译的基础上,我们再做一些改动,注意在这之前不要执行make clean,否则不能发现新问题。

    /×    define.h    */
    #ifndef __DEFINE_H
    #define __DEFINE_H
    
    #include "other.h"
    
    #endif/*__DEFINE_H*/
    
    
    /*   other.h    */
    #ifndef __OTHER_H
    #define __OTHER_H
    
    #define HELLO "hello"
    
    #endif /*__OTHER_H*/

    从结果看,尽管foo.c和main.c都被重新编译了,但依赖关系却没有重新构建。运行complicated结果,其打印结果是我们所希望的“hello”。

    现在,我们对other.h文件进行修改把hello改成hi,从下面运行结果来看,项目并没有因为更改了other文件而重新编译。

    #ifndef __OTHER_H
    #define __OTHER_H
    
    #define HELLO "hi"
    
    #endif /*__OTHER_H*/

    更改Makefile如下,更改部分红色标出了,其实只增加了一个$@:

    .PHONY: all clean
    
    MKDIR = mkdir
    RM = rm
    RMFLAGS = -rf
    
    CC=gcc
    
    DIR_OBJS=objs
    DIR_EXES=exes
    DIR_DEPS=deps
    
    DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
    
    EXE=complicated
    EXE:=$(addprefix $(DIR_EXES)/,$(EXE))
    SRCS=$(wildcard *.c)
    OBJS=$(SRCS:.c=.o)
    OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS))
    DEPS=$(SRCS:.c=.dep)
    DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS))
    
    ifeq ($(wildcard $(DIR_OBJS)),)
    DEP_DIR_OBJS :=$(DIR_OBJS)
    endif#dir_objs
    ifeq ($(wildcard $(DIR_EXES)),)
    DEP_DIR_EXES :=$(DIR_EXES)
    endif#dir_exes
    ifeq ($(wildcard $(DIR_DEPS)),)
    DEP_DIR_DEPS :=$(DIR_DEPS)
    endif#dir_deps
    all: $(EXE)
    
    include $(DEPS)
    
    $(DIRS):
        $(MKDIR) $@
    $(EXE):$(DEP_DIR_EXES) $(OBJS)
        $(CC) -o $@ $(filter %.o,$^)
    $(DIR_OBJS)/%.o:$(DEP_DIR_OBJS) %.c 
        $(CC) -o $@ -c $(filter %.c,$^)
    $(DIR_DEPS)/%.dep:$(DEP_DIR_DEPS) %.c
        @echo "Creating $@ ..."
        @set -e;
        $(RM) $(RMFLAGS) $@.tmp;
        $(CC) -E -MM $(filter %.c,$^) > $@.tmp;
        sed 's,(.*).o[:]*,objs/1.o $@:,g' <$@.tmp >$@;
        $(RM) $(RMFLAGS) $@.tmp
    
    clean:
        $(RM) $(RMFLAGS) $(DIRS)

    这样之后,Makefile就可以知道other.h的更改了。因为$@表示的是依赖关系的文件名,这个问题(Makefile 6随笔中要把foo.h加在依赖关系中的更改一样)和之前的那个问题解决方案一样,用sed命令可以做到,问题的根本在于,我们应该为依赖文件增加依赖关系,这样才能将整个项目全局联系在一起。

    但是,这样之后,连续执行两次make clean:

    第一次clean时,没什么问题,也是我们期望的,第二次clean时,make会先构建依赖项,紧接着又把目录删除。为什么第二次clean时,make会重新构建依赖文件?因为我们有一个include指令,他会优先于目标执行!!!

    为了解决这个问题,我们再运用条件语法,并且用到我们之前提到的MAKECMDGOALS变量。更改后的Makefile如下:

    .PHONY: all clean
    
    MKDIR = mkdir
    RM = rm
    RMFLAGS = -rf
    
    CC=gcc
    
    DIR_OBJS=objs
    DIR_EXES=exes
    DIR_DEPS=deps
    
    DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
    
    EXE=complicated
    EXE:=$(addprefix $(DIR_EXES)/,$(EXE))
    SRCS=$(wildcard *.c)
    OBJS=$(SRCS:.c=.o)
    OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS))
    DEPS=$(SRCS:.c=.dep)
    DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS))
    
    ifeq ($(wildcard $(DIR_OBJS)),)
    DEP_DIR_OBJS :=$(DIR_OBJS)
    endif#dir_objs
    ifeq ($(wildcard $(DIR_EXES)),)
    DEP_DIR_EXES :=$(DIR_EXES)
    endif#dir_exes
    ifeq ($(wildcard $(DIR_DEPS)),)
    DEP_DIR_DEPS :=$(DIR_DEPS)
    endif#dir_deps
    
    all: $(EXE)
    ifneq ($(MAKECMDGOALS),clean)
    include $(DEPS)
    endif#clean
    
    $(DIRS):
        $(MKDIR) $@
    $(EXE):$(DEP_DIR_EXES) $(OBJS)
        $(CC) -o $@ $(filter %.o,$^)
    $(DIR_OBJS)/%.o:$(DEP_DIR_OBJS) %.c 
        $(CC) -o $@ -c $(filter %.c,$^)
    $(DIR_DEPS)/%.dep:$(DEP_DIR_DEPS) %.c
        @echo "Creating $@ ..."
        @set -e;
        $(RM) $(RMFLAGS) $@.tmp;
        $(CC) -E -MM $(filter %.c,$^) > $@.tmp;
        sed 's,(.*).o[:]*,objs/1.o $@:,g' <$@.tmp >$@;
        $(RM) $(RMFLAGS) $@.tmp
    
    clean:
        $(RM) $(RMFLAGS) $(DIRS)

    这样就解决了。

    再看一个例子:

    all:
        @echo "command of rule"
    
    all: dep
    
    dep:
        @echo "prerequisite of rule"

    这个make的特性说明了一个问题:在生成的依赖关系文件中,其中的规则只描述了依赖关系,而没有任何的命令,make是怎么知道使用哪些命令进行目标构建的呢?

    当一个Makefile中存在构建同一目标的不同规则时,make会将这些规则合在一起,合并的内容包括先决条件和命令。尽管在自动生成的依赖关系文件中只存在目标和先决条件,但是由于Makefile中已经定义了.o 和.dep文件的生成规则,因此make会将这两部分结合在一起,从而形成最终针对一个(类)构建目标的规则。上面的那个例子可以很好地帮助我们理解这个make特性。

  • 相关阅读:
    [转帖]ExtJs与服务器的交互(一)
    Ext_两种处理服务器端返回值的方式
    命令行调用dubbo远程服务
    【转】Lombok Pojo默认初始值问题
    【转】Python之可变参数,*参数,**参数,以及传入*参数,进行解包
    Python获取并输出当前日期时间
    idea上 实现了Serializable接口,要自动生成serialVersionUID的方法
    mac下python2.x和python3.x的安装方法和升级方法/卸载
    工程代码不编译src的java目录下的xml文件问题及解决
    Spring Aop织入点语法
  • 原文地址:https://www.cnblogs.com/Liu-Jing/p/8298801.html
Copyright © 2011-2022 走看看