热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android.mk(4)依赖:目标编程的模式

https:www.jianshu.comp3777a585a8d0另一种范式我一直觉得,Makefile确实是CC++程序员的良配,因为Makefile所使

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  # 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 := # 自动 $(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
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

推荐阅读
  • 提升工作效率:掌握这些技巧,IDEA 使用效率翻倍 | IDEA 高效操作指南
    提升工作效率:掌握这些技巧,IDEA 使用效率翻倍 | IDEA 高效操作指南 ... [详细]
  • Python学习:环境配置与安装指南
    Python作为一种跨平台的编程语言,适用于Windows、Linux和macOS等多种操作系统。为了确保本地已成功安装Python,用户可以通过终端或命令行界面输入`python`或`python3`命令进行验证。此外,建议使用虚拟环境管理工具如`venv`或`conda`,以便更好地隔离不同项目依赖,提高开发效率。 ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • JVM参数设置与命令行工具详解
    JVM参数配置与命令行工具的深入解析旨在优化系统性能,通过合理设置JVM参数,确保在高吞吐量的前提下,有效减少垃圾回收(GC)的频率,进而降低系统停顿时间,提升服务的稳定性和响应速度。此外,本文还将详细介绍常用的JVM命令行工具,帮助开发者更好地监控和调优JVM运行状态。 ... [详细]
  • Django框架下的对象关系映射(ORM)详解
    在Django框架中,对象关系映射(ORM)技术是解决面向对象编程与关系型数据库之间不兼容问题的关键工具。通过将数据库表结构映射到Python类,ORM使得开发者能够以面向对象的方式操作数据库,从而简化了数据访问和管理的复杂性。这种技术不仅提高了代码的可读性和可维护性,还增强了应用程序的灵活性和扩展性。 ... [详细]
  • 本文探讨了将PEBuilder转换为DIBooter.sh的方法,重点介绍了如何将DI工具集成到启动层,实现离线镜像引导安装。通过使用DD命令替代传统的grub-install工具,实现了GRUB的离线安装。此外,还详细解析了bootice工具的工作原理及其在该过程中的应用,确保系统在无网络环境下也能顺利引导和安装。 ... [详细]
  • 对于以压缩包形式发布的软件,其目录中通常包含一个配置脚本 `configure`。该脚本的主要功能是确定编译所需的各项参数,如头文件的位置和链接库的路径,并生成相应的 `Makefile` 以供编译使用。通过运行此脚本,开发者可以确保软件在不同环境下的正确编译与安装。此外,该脚本还能够检测系统依赖项,进一步提高编译过程的可靠性和兼容性。 ... [详细]
  • 本文详细介绍了 Ansible Ad-Hoc 命令的使用方法,基于官方文档进行了中文翻译。Ad-Hoc 命令允许用户通过 `usr/bin/ansible` 快速执行一次性任务,适用于快速部署、配置管理和故障排查等场景。文中通过多个实例演示了 Ad-Hoc 命令的具体应用,帮助读者更好地理解和掌握这一强大工具。 ... [详细]
  • Spring Security 认证模块的项目构建与初始化
    本文详细介绍了如何构建和初始化Spring Security认证模块的项目。首先,通过创建一个分布式Maven聚合工程,该工程包含四个模块,分别为core、browser(用于演示)、app等,以构成完整的SeehopeSecurity项目。在项目构建过程中,还涉及日志生成机制,确保能够输出关键信息,便于调试和监控。 ... [详细]
  • PHP中元素的计量单位是什么? ... [详细]
  • 本文详细解析了 MySQL 5.7.20 版本中二进制日志(binlog)崩溃恢复机制的工作流程。假设使用 InnoDB 存储引擎,并且启用了 `sync_binlog=1` 配置,文章深入探讨了在系统崩溃后如何通过 binlog 进行数据恢复,确保数据的一致性和完整性。 ... [详细]
  • 在VC++开发过程中,若遇到致命错误C1010,提示在搜索预编译头文件时遇到意外的文件结束,通常是因为未在源文件中包含必要的预编译头文件“stdafx.h”。为解决此问题,建议检查并确保在源代码的开头处正确添加了该预编译头文件的引用。此外,还可以通过项目属性设置中的预编译头选项进行配置,以确保编译器能够正确识别和处理预编译头文件。 ... [详细]
  • 【Python爬虫实操】 不创作小说,专精网站内容迁移,超高效!(含源代码)
    本文详细介绍了如何利用Python爬虫技术实现高效网站内容迁移,涵盖前端、后端及Android相关知识点。通过具体实例和源代码,展示了如何精准抓取并迁移网站内容,适合对Python爬虫实战感兴趣的开发者参考。 ... [详细]
  • 如何解决在 CentOS 5.8 系统中使用云服务器 ECS 执行 mkfs.ext4 时出现“command not found”错误
    如何解决在 CentOS 5.8 系统中使用云服务器 ECS 执行 mkfs.ext4 时出现“command not found”错误 ... [详细]
  • 在前一篇文章中,我们介绍了如何使用Requests库发送GET请求。本文将深入探讨如何通过Requests库发送POST请求,包括参数格式、请求封装等关键技巧,并通过“历史上的今天”API实例进行详细说明。 ... [详细]
author-avatar
qaz2502919927
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有