• 通用Makefile



    title: 通用Makefile
    date: 2019/3/18 19:03:23
    toc: true

    通用Makefile

    引入与参考

    内核代码在make的时候,使用make V=1,能够显示出具体的信息,我们可以参考内核的makefile来实现一个通用的Makefile

    规则

    最基本的规则最核心的是目标与依赖,当我们执行make target的时候,会去判断后面的xxx依赖是否更新,如果更新则执行,否则不执行.如果为空的话则一直执行,或者没有target的时候,也执行

    target: xxx
    	dosomething
    

    简单例子

    这里我们有源代码a.c,b.c来生成test,其中a.c包含a.h

    test:a.o b.o
    	gcc -o test a.o b.o
    %.o : %.c 
    	gcc -c -o $@ $<     #$@ 表示目标,这里也就是%.o $< 表示第一个依赖,也就是%.c
    

    依赖

    缺陷

    这里a.c比如包含了a.h,那么在这个规则里,如果a.h改变,test并不会更新,所以我们需要a.c的依赖加入a.h,也就是需要这么修改

    test:a.o b.o
    	gcc -o test a.o b.o
    
    a.o:a.c a.h
    
    %.o : %.c 
    	gcc -c -o $@ $<
    

    那么很明显的,每个c里面的头文件都是其依赖,我们如何自动生成依赖?

    自动生成依赖

    尝试如下命令

    gcc -c -o a.out a.c -Wp,-MD,a.d    #-c 编译不链接
    

    可以看到自动生成如下依赖a.d文件,格式就是a.o: a.c 头文件列表

    a.o: a.c /usr/include/stdc-predef.h /usr/include/stdio.h 
     /usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h 
     /usr/include/x86_64-linux-gnu/bits/wordsize.h 
     /usr/include/x86_64-linux-gnu/gnu/stubs.h 
     /usr/include/x86_64-linux-gnu/gnu/stubs-64.h 
     /usr/lib/gcc/x86_64-linux-gnu/5/include/stddef.h 
     /usr/include/x86_64-linux-gnu/bits/types.h 
     /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h 
     /usr/include/_G_config.h /usr/include/wchar.h 
     /usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h 
     /usr/include/x86_64-linux-gnu/bits/stdio_lim.h 
     /usr/include/x86_64-linux-gnu/bits/sys_errlist.h a.h
    

    进一步优化这个Makefile,这样就会生成依赖了

    test:a.o b.o
    	gcc -o test a.o b.o
    
    a.o:a.c a.h
    
    %.o : %.c 
    	gcc -c -Wp,-MD,$@.d  -o $@ $<
    

    使用自动生成的依赖

    直接上例子

    objs := a.o b.o
    
    test:$(objs)
    	gcc -o test $^ 	#所有依赖
    
    # 遍历obj的每一个元素 也就是 f=a.o f=b.o
    # 然后命名为 .f.d 也就是 .a.o.d .b.o.d
    dep_files := $(foreach f,$(objs),.$(f).d)
    
    # wildcard 判断是否存在文件
    # 在Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。
    # 所以这个生成的就是文件列表了
    dep_files := $(wildcard $(dep_files))
    
    # 这里直接include 的是一系列的文件
    ifneq ($(dep_files),)
      include $(dep_files)
    endif
    
    # 第一次的时候,没有依赖文件生成
    # 第二次的时候,存在了新的依赖文件,我们上面的include 已经有了 a.o: 依赖的c 依赖的h
    %.o : %.c 
    	gcc -Wp,-MD,.$@.d -c -o $@ $<
    
    clean:
    	rm *.o test
    

    流程也就是

    1. 第一次的时候,没有依赖文件,全编译,同时生成.d的依赖文件
    2. 第二次的时候,已经有依赖文件,我们会include这个文件,文件的内容形如a.o: a.c a.h xxx,这就是完整的依赖了
    3. 依靠%.o %.c 来执行编译了

    目录遍历

    同一个目录下,先编译子目录,再编译同级的文件,也就是先编译,b.c再a.c

    a.c
    /b
    	/b.c
    

    顶层的Makefile

    
    CROSS_COMPILE = arm-linux-
    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
    
    # -g 调试信息 -O2 优化等级 -Wall 显示所有警告
    CFLAGS := -Wall -O2 -g
    CFLAGS += -I $(shell pwd)/include
    
    LDFLAGS := -lm -lfreetype
    
    export CFLAGS LDFLAGS
    
    TOPDIR := $(shell pwd)
    export TOPDIR
    
    TARGET := show_file
    
    obj-y += main.o
    obj-y += display/
    obj-y += draw/
    obj-y += encoding/
    obj-y += fonts/
    
    all : 
    	# -f 或者 -file 是指定makefile的文件,也就是只用指定的makefile来执行接下去的make
    	make -C ./ -f $(TOPDIR)/Makefile.build
    	$(CC) $(LDFLAGS) -o $(TARGET) built-in.o
    
    clean:
    	rm -f $(shell find -name "*.o")
    	rm -f $(TARGET)
    
    distclean:
    	rm -f $(shell find -name "*.o")
    	rm -f $(shell find -name "*.d")
    	rm -f $(TARGET)
    	
    

    顶层的Makefile.build

    # 伪目标,也就是总是执行的意思
    PHONY := __build
    __build:
    
    obj-y :=
    subdir-y :=
    
    # 包含了当前的Makefile,也就是引入了顶层定义的目标和编译工具链
    include Makefile
    
    
    # 目的:提取目录名,去除/
    # 1. $(filter %/, $(obj-y)) 这个就是保留带有 /结尾的元素 也就是 c/ d/ ,筛选出目录
    # 2. $(patsubst %/,%,xxx)   这个xxx就是上面调出来的 c/ d/
    # 2. 然后使用替换 把%/ 替换成 %  也就是变成  c d,提取目录的名字
    __subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
    subdir-y	+= $(__subdir-y)
    
    # 标记1---
    # 对于每个当前目录下的子目录,会生成子目录下的 c/built-in.o  d/built-in.o
    subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
    
    # 这里筛选出当前目录下的文件 a.o
    cur_objs := $(filter-out %/, $(obj-y))
    # 遍历当前目录的目标文件,产生依赖 .xxx.d
    dep_files := $(foreach f,$(cur_objs),.$(f).d)
    # 产生文件列表,wildcard 它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表
    dep_files := $(wildcard $(dep_files))
    # 如果文件存在,则包含这些依赖
    ifneq ($(dep_files),)
      include $(dep_files)
    endif
    
    # 当前目录下的子目录的名字 这个PHONY 其实就是一个名字 可以写作PHONY123,但是最下面是指定了他是伪目标
    PHONY += $(subdir-y)
    
    # subdir-y 表示子目录 也就是 c d  这里已经去除了/  subdir-y 这里是一系列的目录名
    __build : $(subdir-y) built-in.o
    	#echo  zzz... $(subdir-y) zzzz.  # 这里就打印出一些列的
    
    # 子目录,使用顶层的Makefile.build 也就是本文件make
    # 这里 -C $@ 也就是说明进入子目录,使用本文件make
    # 可以看出来,其实 $(subdir-y) 是个每次都执行的目标,也就是每次都是进入子目录,尝试运行
    $(subdir-y):
    	make -C $@ -f $(TOPDIR)/Makefile.build
    
    # 依赖为本目录下的  cur_objs目标文件,和 subdir_objs,这个是子目录的c/built-in.o d/built-in.o
    # 在标记1处生成的
    built-in.o : $(cur_objs) $(subdir_objs)
    	$(LD) -r -o $@ $^
    
    # $@ 目标
    dep_file = .$@.d
    
    # 编译 产生了.目标.d的依赖
    %.o : %.c
    	$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<
    	
    .PHONY : $(PHONY)
    

    子目录的Makefile

    obj-y += a.o	# 表示当前的目标文件
    obj-y += c/		# 这里的/ 表示c是个子目录,会继续进去编译的
    

    总结

    1. 我们先进入最底层的一个没有子目录的目录,生成一个built-in.o
    2. 然后回到上一层,如果还有其他子目录,则继续进入子目录
    3. 本级的子目录都处理完,那么与本目录其他的目标再链接生成一个新的built-in.o
    4. ...
    5. 回到顶层,与顶层的目标再链接生成built-in.o
    6. 最后再链接生成target

    mark

    Tips

    • 我们可以使用gcc -c -o a.out a.c -Wp,-MD,a.d #-c 编译不链接 来查看头文件路径
    • := 可以追加,=确定的不能追加
  • 相关阅读:
    FreeBSD使用多线程下载工具axel
    类UNIX系统基础:文件安全与权限
    基于pf防火墙控制IP连接数
    在FreeBSD下搭建高性能企业级网关与代理服务器
    搭建自己的CVSup服务器
    转:Spring技术内幕——深入解析Spring架构与设计原理(三)IOC实现原理
    Spring Web MVC的实现(二)
    java中HashSet详解
    转:Spring技术内幕——深入解析Spring架构与设计原理(二)IOC实现原理
    DIV垂直水平都居中
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10554980.html
Copyright © 2020-2023  润新知