zoukankan      html  css  js  c++  java
  • Android.mk(4) 依赖:目标编程的模式

    https://www.jianshu.com/p/3777a585a8d0

    另一种范式

    我一直觉得,Makefile确实是C/C++程序员的良配,因为Makefile所使用的两种范式都是C/C++程序员不熟悉的,一种是函数式的思想,一种是依赖构成的目标链的模式。

    Makefile从最基本上来说,可以抽象成下面这样的:

        target ... : prerequisites ...
                command
                ...
                ...
    

    如大家所熟悉的,这段的意义是:当prerequisites有更新的时候,执行command命令。如果target是一个真实的目标,也就是对应一个真实的文件,那么就生成这个文件。如果是伪目标,可以被用来做为一个入口,比如clean,也可以成为一个真实目标的依赖。
    可以明显地分为两个部分:一个是target依赖链的范式,这与过程式语言的C语言非常不同。用蒋军的话讲,跟Prolog有点像。有着它自己的一套逻辑系统。
    后面的command,我们前面讲了不少了,我个人是希望大家以函数式的思想来写。

    我们落地到一个实际的例子中:

    $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
        $(call build-systemimage-target,$@)
    

    这个$(BUILT_SYSTEMIMAGE),是个真实的目标,对应了要生成的文件system.img,如下:

    BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img
    

    下面来看看,system.img所依赖目标,先看第一个,结果这一个实际上又是两个:

    FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)
    

    然后,我们发现这个依赖在一层层地扩张:

    INTERNAL_SYSTEMIMAGE_FILES := $(filter $(TARGET_OUT)/%, 
        $(ALL_PREBUILT) 
        $(ALL_COPIED_HEADERS) 
        $(ALL_GENERATED_SOURCES) 
        $(ALL_DEFAULT_INSTALLED_MODULES) 
        $(PDK_FUSION_SYSIMG_FILES) 
        $(RECOVERY_RESOURCE_ZIP))
    

    扩张还在继续,比如,对于ALL_PREBUILT,各个模块不断地把自己的东西增加进去:

    ALL_PREBUILT += $(TARGET_OUT)/bin/monkey
    

    其他的目标原理相通,这里就不多浪费篇幅了。
    总而言之,这一大套目标中,只要有任何一个有变化,system.img就要重新生成了。如何生成?在下一行中写着呢:调用build-systemimage-target啊。

    Makefile写作指南

    目标式和函数式两种范式都学好了,下面是我们如何组织材料来完成我们的工程的时候了。

    1. 先定义总目标
      Makefile的总的目的是输出一个或多个结果文件,先把总的目标定义好。
      然后假设子目标都已经构建好了,下面写一个将这些中间产品变成最终目标的脚本。
      比如编译一个简单的C程序,总的目标是一个可执行文件,最终加工时的材料是已经编译好的.o文件和输入的第三方的库文件等,我们先不管它们是如何编译的,假设它们已经做好了,我们只要写一个链接的脚本就好了。
      像我们上面的system.img的例子,反正依赖多,就分门别类的列吧,最终我们只需要把它们打个包就好了。
    2. 层层分解,逐步完成
      然后去寻找,构成这个大目标的第一层的构件是什么,像上面我们所看过的一样,逐层扩张。
      对于C文件,这时候才考虑每一个.o是如何从源文件编译的。
    3. 模块化、函数化
      上面两步都是目标模式的,这一步开始搞函数模式了。将各目标中可重用的函数抽象出来,该分文件就分文件,该整理代码就整理代码等
    4. 测试调优
      当一个工程大到一定程度的时候,Makefile的可读性会严重下降。
      这时候我们还是按目标式和函数式两条主线来降低复杂度。目标是层次式的,我们可以一层一层地调试,比如先调从.c到.o的编译过程,再调将.o链接起来的总装部分.
      哪一个子模块出问题,就专门调那一部分的。
      对于功能部分,我们一直强调函数式思想就是希望,对于某一个确实性的输入,能有一个确定性的输出,没有副作用,这样能够将调试的难度降低,我们可以一个函数一个函数地调试。
      Makefile的调试以打日志为主,还可以通过make -p来输出完整的变量和目标列表。

    make -p,看看make都做了些什么

    下面是我在cygwin下的make -p的输出结果的节选

    Make工具的信息

    首先是Make工具汇报下自己的基础情况:

    The files is:main.cpp
    # GNU Make 4.1
    # Built for x86_64-unknown-cygwin
    # Copyright (C) 1988-2014 Free Software Foundation, Inc.
    # License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    # This is free software: you are free to change and redistribute it.
    # There is NO WARRANTY, to the extent permitted by law.
    
    # make 数据基础,打印在 Tue May  3 17:54:36 2016
    

    变量

    下面是变量的列表,包含我们自己定义的,也包含make自动为我们生成的。

    # 变量
    
    # 'override' directive
    GNUMAKEFLAGS := 
    # 自动
    <D = $(patsubst %/,%,$(dir $<))
    # 自动
    ?F = $(notdir $?)
    # 默认
    .SHELLFLAGS := -c
    # makefile (from 'Makefile', line 30)
    result_findString2 := 
    # makefile
    MAKEFLAGS = p
    # 默认
    CWEAVE = cweave
    # 自动
    ?D = $(patsubst %/,%,$(dir $?))
    # 环境
    !:: = ::
    # 自动
    @D = $(patsubst %/,%,$(dir $@))
    # 环境
    HOMEDRIVE = C:
    # 自动
    @F = $(notdir $@)
    # 自动
    ^D = $(patsubst %/,%,$(dir $^))
    # makefile
    CURDIR := /cygdrive/d/working/codeBlocks/Hello
    # makefile
    SHELL = /bin/sh
    # 默认
    RM = rm -f
    # 默认
    CO = co
    ...
    

    目录信息

    # 目录
    
    # SCCS:无法对其进行 stat 操作。
    # . (设备 114478965,i-节点 1688849860268365):10 文件, 19 不可能.
    # RCS:无法对其进行 stat 操作。
    
    # 10 文件, 19 不可能在 3 目录中。
    

    隐含规则信息

    # 隐含规则。
    
    %.out:
    
    %.a:
    
    %.ln:
    
    %.o:
    
    %: %.o
    #  recipe to execute (内置):
        $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    %.c:
    
    %: %.c
    #  recipe to execute (内置):
        $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    %.ln: %.c
    #  recipe to execute (内置):
        $(LINT.c) -C$* $<
    
    %.o: %.c
    #  recipe to execute (内置):
        $(COMPILE.c) $(OUTPUT_OPTION) $<
    
    %.cc:
    
    %: %.cc
    #  recipe to execute (内置):
        $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    %.o: %.cc
    #  recipe to execute (内置):
        $(COMPILE.cc) $(OUTPUT_OPTION) $<
    
    %.C:
    
    %: %.C
    #  recipe to execute (内置):
        $(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    %.o: %.C
    #  recipe to execute (内置):
        $(COMPILE.C) $(OUTPUT_OPTION) $<
    
    %.cpp:
    
    %: %.cpp
    #  recipe to execute (内置):
        $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    %.o: %.cpp
    #  recipe to execute (内置):
        $(COMPILE.cpp) $(OUTPUT_OPTION) $<
    
    %.p:
    
    %: %.p
    #  recipe to execute (内置):
        $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    %.o: %.p
    #  recipe to execute (内置):
        $(COMPILE.p) $(OUTPUT_OPTION) $<
    ...
    

    文件目标和假目标

    # 文件
    
    # 不是一个目标:
    .web.p:
    #  Builtin rule
    #  对隐含规则的搜索尚未完成。
    #  从不检查修改时间。
    #  文件尚未被更新。
    #  recipe to execute (内置):
        $(TANGLE) $<
    
    # 不是一个目标:
    .l.r:
    #  Builtin rule
    #  对隐含规则的搜索尚未完成。
    #  从不检查修改时间。
    #  文件尚未被更新。
    #  recipe to execute (内置):
        $(LEX.l) $< > $@ 
         mv -f lex.yy.r $@
    
    all8:
    #  假目标 (.PHONY的前提)。
    #  对隐含规则的搜索尚未完成。
    #  文件不存在。
    #  文件尚未被更新。
    #  recipe to execute (from 'Makefile', line 66):
        @echo $(filter-out default interpreter jit optimizing,xoc)
        @echo $(filter-out default interpreter jit optimizing,default)
    
    all9:
    #  假目标 (.PHONY的前提)。
    #  对隐含规则的搜索尚未完成。
    #  文件不存在。
    #  文件尚未被更新。
    #  recipe to execute (from 'Makefile', line 75):
        $(eval ARCH_OF_BOOT_OAT := $(lastword $(subst /, ,$(dir $(BOOT_ART_SRC)))))
        $(eval OAT_TEMP := $(PRODUCT_OUT)/data/dalvik-cache/temp-oat/system/framework/$(ARCH_OF_BOOT_OAT))
        $(eval OAT_SRC := $(patsubst %.art,%.oat,$(BOOT_ART_SRC)))
        $(eval OAT_DIST := $(patsubst %.art,%.oat,$(BOOT_ART_DST)))
        @echo $(ARCH_OF_BOOT_OAT)
        @echo $(OAT_TEMP)
        @echo $(OAT_SRC)
        @echo $(OAT_DIST)
    ...
    


    作者:Jtag特工
    链接:https://www.jianshu.com/p/3777a585a8d0
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    关于Cocos2d-x开发一个游戏的过程自述
    关于JDK环境变量的配置问题
    lnmp、lamp、lnmpa一键安装包(Updated: 2015-10-25)
    成大事者必备的九种能力、九种手段、九种心态
    燕十八mysql笔记
    cmake 安装 mysql5.5 版本
    IOS 中微信 网页授权报 key[也就是code]失效 解决办法
    js 复制 功能
    php 二维数组排序
    php获取前一天,前一个月,前一年的时间
  • 原文地址:https://www.cnblogs.com/onelikeone/p/8438267.html
Copyright © 2011-2022 走看看