• [置顶] 自己写一个简单通用的Makefile


    转自:http://blog.csdn.net/u011913612/article/details/52102241

    一.makefile的作用

              Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中记录有文件的信 息,在make时会决定在链接的时候需要重新编译哪些文件。Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动发现最终的生成文件已经过时,而应该重新编译相应的模块。 makefile带来的好处就是—"自动化编译",一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。默认的情况下,make命令会在当前目录下按顺序找寻文件名为"GNUmakefile"、"makefile"、"Makefile"的文件,找到了解释这个文件。当然也可以使用make -f DIR/makefile 来指定用于makefile文件

    二.makefile的几点基础知识

          1.赋值符号的区别
             =  是最基本的赋值,用到了之后才赋值,不能在变量后追加内容
         := 是覆盖之前的值,立即赋值,可以在变量后追加内容
        ?= 是如果没有被赋值过就赋予等号后面的值
        += 是添加等号后面的值

    2.自动变量

        $<    第一个依赖文件的名称
        $?    所有的依赖文件,以空格分开,这些依赖文件的修改日期比目    标的创建日期晚
        $@  目标的完整名称
        $^    所有的依赖文件,以空格分开,不包含重复的依赖文件

    3.几个常用的函数

    1. $(patsubst %.c,%.o,x.c.c bar.c)
    把字串“x.c.cbar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.obar.o”
    2.$(filter <pattern...>,<text> )
    以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。 
    3.$(filter-out <pattern...>,<text> )
    4.$(foreach <var>,<list>,<text> )
    把参数<list>中的单词逐一取出放到参数<var>所指定的变量中, 然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环这个过程。
    5.shell函数,例如files := $(shell echo *.c)

    三.通用Makefile的编译过程

           从顶层开始递归进入子目录,当进入到一个目录的最底层时,开始使用编译器编译,再将该层的所有.o文件打包成build-in.o,返回它的上一层目录再递归进入子目录,当编译完所有的子目录后,就开始编译顶层的.c文件,最后将顶层的.o文件和顶层每个子目录的build-in.o链接成我们的目标文件。

    思维导图:


    四、实战

    假如有这样一个目录结构的工程

    a

    ----d

    ----a.c

    b

    ----b.c

    c

    ----c.c

    main.c



    顶级Makefile:

    CROSS_COMPILE =
    AS		= $(CROSS_COMPILE)as
    LD		= $(CROSS_COMPILE)ld
    CC		= $(CROSS_COMPILE)gcc
    CPP		= $(CC) -E
    AR		= $(CROSS_COMPILE)ar
    NM		= $(CROSS_COMPILE)nm
    
    STRIP		= $(CROSS_COMPILE)strip
    OBJCOPY		= $(CROSS_COMPILE)objcopy
    OBJDUMP		= $(CROSS_COMPILE)objdump
    
    export AS LD CC CPP AR NM
    export STRIP OBJCOPY OBJDUMP
    
    CFLAGS := -Wall -O2 -g
    
    LDFLAGS := 
    
    export CFLAGS LDFLAGS
    
    TOPDIR := $(shell pwd)
    export TOPDIR
    
    TARGET := test
    
    
     obj-y += main.o
     obj-y += a/
     obj-y += b/
     obj-y += c/
    
    all : 
    	make -C ./ -f $(TOPDIR)/Makefile.build
    	$(CC) $(LDFLAGS) -o $(TARGET) built-in.o
    
    
    clean:
    	rm -f $(shell find -name "*.o")
    	rm -f $(shell find -name "*.d")
    	rm -f $(TARGET)
    .PHONY:all clean 
    


    这里前面就是定义一些变量,all是工程默认的目标,它是一个伪目标,进入伪目标后执行的命令就是执行Makefile.build,这里就会引起递归调用,在Makefile.build中又会调用Makefile.build.一直到Makefile.build返回以后,会使用Makefile.build最后生成的built-in.o生成最终的目标文件。

    Makefile.build:

    PHONY := build
    build :
    obj-y :=
    subdir-y :=
    include Makefile
    __subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
    subdir-y	+= $(__subdir-y)
    subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
    cur_objs := $(filter-out %/, $(obj-y))
    dep_files := $(foreach f,$(cur_objs),.$(f).d)
    #dep_files := $(wildcard $(dep_files))
    #ifneq ($(dep_files),)
    #  include $(dep_files)
    #endif
    PHONY += $(subdir-y)
    build : $(subdir-y) built-in.o
    $(subdir-y):
    	make -C $@ -f $(TOPDIR)/Makefile.build
    built-in.o : $(cur_objs) $(subdir_objs)
    	$(LD) -r -o $@ $^
    dep_file = .$@.d
    %.o : %.c
    	$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<
    .PHONY : $(PHONY)

    Makefile.build会加载顶级目录下的Makefile,在顶级目录下的Makefile中已经给obj-y添加了一些条目,subdir-y就是获取子目录,然后对每一个子目录又调用Makefile.build。

    当递归到没有子目录的目录时,Makefile.build开始返回,并使用$(CC)对源文件进行编译,将所有的.c生成.o文件,并将当前目录下的.o和子目录下的build-in.o连接成当前目录下的build-in.o,并回返上级目录,一次往复,最终返回到顶级目录,在顶级目录下生成build-in.o。返回到顶级目录后,Makefile.build返回到了Makefile中,Makefile在使用build-in.o生成指定的目标文件。至此,递归结束,整个系统编译完成。

    这里的-MD选项和.$@.d的作用是 生成头文件的依赖关系时,把依赖关系写入到这个文件中去。

    要想彻底看懂的话,建议认真推倒下每一个变量的值,比对目录与文件,会更好理清编译的过程。

    子目录下的Makefile:

    obj-y += a.o
    obj-y += d/

    子目录的Makefile就是添加该目录中的文件与子目录,子目录以/结尾。

    验证:

    执行make

    打印如下:

    make -C ./ -f /home/dragon/liujinwei/uni-makefile/Makefile.build
    make[1]: Entering directory `/home/dragon/liujinwei/uni-makefile'
    make -C a -f /home/dragon/liujinwei/uni-makefile/Makefile.build
    make[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/a'
    make -C d -f /home/dragon/liujinwei/uni-makefile/Makefile.build
    make[3]: Entering directory `/home/dragon/liujinwei/uni-makefile/a/d'
    gcc -Wall -O2 -g -Wp,-MD,.d.o.d -c -o d.o d.c
    ld -r -o built-in.o d.o
    make[3]: Leaving directory `/home/dragon/liujinwei/uni-makefile/a/d'
    gcc -Wall -O2 -g -Wp,-MD,.a.o.d -c -o a.o a.c
    ld -r -o built-in.o a.o d/built-in.o
    make[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/a'
    make -C b -f /home/dragon/liujinwei/uni-makefile/Makefile.build
    make[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/b'
    gcc -Wall -O2 -g -Wp,-MD,.b.o.d -c -o b.o b.c
    ld -r -o built-in.o b.o
    make[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/b'
    make -C c -f /home/dragon/liujinwei/uni-makefile/Makefile.build
    make[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/c'
    gcc -Wall -O2 -g -Wp,-MD,.c.o.d -c -o c.o c.c
    ld -r -o built-in.o c.o
    make[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/c'
    gcc -Wall -O2 -g -Wp,-MD,.main.o.d -c -o main.o main.c
    ld -r -o built-in.o main.o a/built-in.o b/built-in.o c/built-in.o
    make[1]: Leaving directory `/home/dragon/liujinwei/uni-makefile'
    gcc  -o test built-in.o
    
    #### make completed successfully  ####


    make之后的目录:

    顶级:


    a目录:


    d目录:




  • 相关阅读:
    C the basics (DMA)
    穷举子集
    排序算法(1)
    C the basics (array, complex)
    Linux中date命令的各种实用方法
    syntaxhighlight实现帝国cms代码高亮/语法高亮(一)
    帝国cms修改评论表情每行显示个数
    java 中hashcode 与 equals的关系
    Java 远程调用与分布式通信的区别
    帝国CMS的phomenewspic/ecmsinfo标签详解
  • 原文地址:https://www.cnblogs.com/alan666/p/8311798.html
Copyright © 2020-2023  润新知