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写作指南
目标式和函数式两种范式都学好了,下面是我们如何组织材料来完成我们的工程的时候了。
- 先定义总目标
Makefile的总的目的是输出一个或多个结果文件,先把总的目标定义好。
然后假设子目标都已经构建好了,下面写一个将这些中间产品变成最终目标的脚本。
比如编译一个简单的C程序,总的目标是一个可执行文件,最终加工时的材料是已经编译好的.o文件和输入的第三方的库文件等,我们先不管它们是如何编译的,假设它们已经做好了,我们只要写一个链接的脚本就好了。
像我们上面的system.img的例子,反正依赖多,就分门别类的列吧,最终我们只需要把它们打个包就好了。 - 层层分解,逐步完成
然后去寻找,构成这个大目标的第一层的构件是什么,像上面我们所看过的一样,逐层扩张。
对于C文件,这时候才考虑每一个.o是如何从源文件编译的。 - 模块化、函数化
上面两步都是目标模式的,这一步开始搞函数模式了。将各目标中可重用的函数抽象出来,该分文件就分文件,该整理代码就整理代码等 - 测试调优
当一个工程大到一定程度的时候,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
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Android.mk(4) 依赖:目标编程的模式的更多相关文章
- Android.mk
Introduction: Android.mk编译文件是用来向Android NDK描述你的C,C++源代码文件的, 这篇文档描述了它的语法.在阅读下面的内容之前,假定你已经阅读了docs/OVER ...
- 【整理修订】Android.mk详解
Android.mk详解 1. Android.mk 的应用范围 Android.mk文件是GNU Makefile的一小部分,它用来对Android程序进行编译. 一个Android.mk文件可以编 ...
- android.mk android源码编译
http://www.cnblogs.com/chenbin7/archive/2013/01/05/2846863.html Android.mk简单分析 2013-01-05 22:51 by . ...
- Android.mk简介<转>
文章参照:http://blog.sina.com.cn/s/blog_67d8d7060100q8un.html Android.mk文件是GNU Makefile的一小部分,它用来对Android ...
- Android.mk简介
http://www.cnblogs.com/hnrainll/archive/2012/12/18/2822711.html Android.mk文件是GNU Makefile的一小部分,它用来对A ...
- Android编译系统(Android.mk文件详解)
[Android-NDK(Native Development Kit) docs文档] NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成a ...
- Android.mk基础
1.前言 Android.mk用于向编译系统描述源文件和共享库,它实际上是编译系统解析一次或多次的微小GNU makefile片段.它的语法支持将源文件分组为模块,模块是静态库.共享库或独立的可执行文 ...
- Android.mk相关知识
Android.mk是Android提供的一种makefile文件,用来指定诸如编译生成so库名.引用的头文件目录.需要编译的.c/.cpp文件和.a静态库文件等.要掌握jni,就必须熟练掌握Andr ...
- Android.mk详解
Android.mk是Android提供的一种makefile文件,用来指定诸如编译生成so库名.引用的头文件目录.需要编译的.c/.cpp文件和.a静态库文件等.要掌握jni,就必须熟练掌握Andr ...
随机推荐
- 实现linux服务器之间无密码互访
最近老是在群里看到许多同学问,linux之间无密码互访怎么弄啊,百度的看得我头晕之类的,所以我就写写怎么样在两台linux服务器之间实现无密码互访,也就是让它们互相信任的意思,废话不多说,直接上菜. ...
- ubuntu14.04_64位安装tensorflow-gpu
第一步(可直接跳到第二步):安装nvidia显卡驱动 linux用户可以通过官方ppa解决安装GPU驱动的问题.使用如下命令添加Graphic Drivers PPA: sudo add-apt-re ...
- iOS : 用 InterfaceBuilder 开始一个项目
1.创建一个 xib 文件 : Main_iPhone.xib 更改 File's Owner 的 Class 为 UIApplication; 添加 1 个 Window .1 个 Object . ...
- 在PC上运行安卓(Android)应用程序的几个方法
三种方法: 1.在PC安装一个安卓模拟器,在模拟器里面运行apk: 2.虚拟机安装 Android x86 然后在此系统里运行: 3.利用谷歌chrome浏览器运行(这是一个新颖.有前途.激动人心的方 ...
- [转]Python多线程与多线程中join()的用法
https://www.cnblogs.com/cnkai/p/7504980.html Python多线程与多进程中join()方法的效果是相同的. 下面仅以多线程为例: 首先需要明确几个概念: 知 ...
- SSM框架快速整合实例——学生查询
一.快速准备 SSM 框架即 Spring 框架.SpringMVC 框架.MyBatis 框架,关于这几个框架的基础和入门程序,我前面已经写过几篇文章作为基础和入门介绍了.这里再简单的介绍一下: 1 ...
- Oracle:oracle 12.1.0.2 升级到12.2.0.1 后,自动任务报错:ORA-20001: Statistics Advisor: Invalid task name for the current user
具体错误如下:关键字:ORA-12012.ORA-20001 ORA-12012: error on auto execute of job "SYS"."ORA$AT_ ...
- Lua 中 number 转换各种进制,以及string串转number
原文地址:http://blog.csdn.net/david_dai_1108/article/details/71699449 --region : NumConvert.lua --Date : ...
- 查询SQL阻塞语句
SELECT SPID=p.spid, DBName = convert(CHAR(),d.name), ProgramName = program_name, LoginName = convert ...
- smartgit 需要输入序列号解决办法
找到路径: %APPDATA%\syntevo\SmartGit\<main-smartgit-version> 然后删除: settings.xml 再重新打开smartgit 就可以了