首页
chatGPT
关于
友链
其它
统计
更多
壁纸
留言
Search
1
cgroup--(4)cgroup v1和cgroup v2的详细介绍
6,404 阅读
2
修改Linux Kernel defconfig的标准方法
6,380 阅读
3
Android系统之VINTF(1)manifests&compatibility matrices
5,970 阅读
4
使用git生成patch和应用patch
3,451 阅读
5
c语言的__attribute__
3,168 阅读
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
工具
软件工具
Bug
COMPANY
登录
Search
标签搜索
shell
Linux
c
uboot
Vim
vintf
Linux驱动
Android
device_tree
git
DEBUG
arm64
链表
数据结构
IDR
内核
ELF
gcc
ARM
网址
adtxl
累计撰写
367
篇文章
累计收到
14
条评论
首页
栏目
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
工具
软件工具
Bug
COMPANY
页面
chatGPT
关于
友链
其它
统计
壁纸
留言
搜索到
367
篇与
的结果
2021-02-24
画图技能之--时序图
转载自程序员必备画图技能之——时序图[TOC]什么是时序图时序图(Sequence Diagram),又名序列图、循序图,是一种 UML 交互图。它通过描述对象之间发送消息的时间顺序显示多个对象之间的动态协作。使用场景时序图的使用场景非常广泛,几乎各行各业都可以使用。当然,作为一个软件工作者,我这边主要列举和软件开发有关的场景。1. 梳理业务流程一般的软件开发都是为了支撑某个具体的业务。有时候业务的流程会比较复杂,涉及到多种角色,这时就可以使用时序图来梳理这个业务逻辑。这样会使业务看起来非常清晰,代码写起来也是水到渠成的事情了。一般的软件开发都是为了支撑某个具体的业务。有时候业务的流程会比较复杂,涉及到多种角色,这时就可以使用时序图来梳理这个业务逻辑。这样会使业务看起来非常清晰,代码写起来也是水到渠成的事情了。2. 梳理开源软件作为一个合格的程序员,阅读源代码的能力一定要过关。一般成熟框架的源代码调用深度都比较深,类之间的调用关系也比较复杂。我喜欢用时序图来梳理框架中这些对象之间的关系。比如再看 Tomcat 启动流程的过程中,我就时序图梳理了各个组件之间的关系,看起来层次非常清楚,也便于记忆。3. 时序图的角色我们在画时序图时会涉及下面7种元素:角色(Actor)对象(Object)生命线(LifeLine)控制焦点(Activation)消息(Message)自关联消息组合片段。其中前6种是比较常用和重要的元素,最后的组合片段元素不是很常用,但是比较复杂。我们先介绍前6种元素,再单独介绍组合片段元素。1. 角色(Actor)系统角色,可以是人或者其他系统和子系统。以一个小人图标表示。2. 对象(Object)对象位于时序图的顶部,以一个矩形表示。对象的命名方式一般有三种:对象名和类名。例如:华为手机:手机、loginServiceObject:LoginService;只显示类名,不显示对象,即为一个匿名类。例如::手机、:LoginSservice。只显示对象名,不显示类名。例如:华为手机:、loginServiceObject:。3. 生命线(LifeLine)时序图中每个对象和底部中心都有一条垂直的虚线,这就是对象的生命线(对象的时间线)。以一条垂直的虚线表示。4. 控制焦点(Activation)控制焦点代表时序图中在对象时间线上某段时期执行的操作。以一个很窄的矩形表示。5. 消息(Message)表示对象之间发送的信息。消息分为三种类型同步消息(Synchronous Message)消息的发送者把控制传递给消息的接收者,然后停止活动,等待消息的接收者放弃或者返回控制。用来表示同步的意义。以一条实线和实心箭头表示。异步消息(Asynchronous Message)消息发送者通过消息把信号传递给消息的接收者,然后继续自己的活动,不等待接受者返回消息或者控制。异步消息的接收者和发送者是并发工作的。以一条实线和大于号表示。返回消息(Return Message)返回消息表示从过程调用返回。以小于号和虚线表示。6. 自关联消息表示方法的自身调用或者一个对象内的一个方法调用另外一个方法。以一个半闭合的长方形+下方实心剪头表示。下面举例一个时序图的列子,看下上面几种元素具体的使用方式。ps:这个图上的返回消息画的不太对,应该是空心箭头7. 组合片段组合片段用来解决交互执行的条件和方式,它允许在序列图中直接表示逻辑组件,用于通过指定条件或子进程的应用区域,为任何生命线的任何部分定义特殊条件和子进程。组合片段共有13种,名称及含义如下:组合名称组合含义ref引用其他地方定义的组合片段alt在一组行为中根据特定的条件选择某个交互opt表示一个可选的行为break提供了和编程语言中的break类拟的机制par支持交互片段的并发执行seq强迫交互按照特定的顺序执行strict明确定义了一组交互片段的执行顺序neg用来标志不应该发生的交互region标志在组合片段中先于其他交互片断发生的交互ignore明确定义了交互片段不应该响应的消息consider明确标志了应该被处理的消息assert标志了在交互片段中作为事件唯一的合法继续者的操作数loop说明交互片段会被重复执行组合片段的功能平时用的不是很多,具体使用时可以参考本文最后关于组合片段的文章,这边不做深入介绍了。参考:https://blog.csdn.net/fly_zxy/article/details/80911942http://baijiahao.baidu.com/s?id=1561926824533534&wfr=spider&for=pchttps://www.cnblogs.com/whylaughing/p/5794693.html
2021年02月24日
898 阅读
0 评论
0 点赞
2021-02-19
vim入门--(3)小改动
暂无简介
2021年02月19日
847 阅读
0 评论
1 点赞
2021-02-07
vim入门--(2)移动
暂无简介
2021年02月07日
768 阅读
0 评论
0 点赞
2021-02-04
Android init.rc文件
最好直接去看Android的官方文档,那里更完整准确,system/core/init/README.md1. 文件简介Android init.rc文件由系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action,Commands,Services,Options.在init.rc文件中一条语句通常是占据一行.单词之间是通过空格符来相隔的.如果需要在单词内使用空格,那么得使用转义字符”\”,如果在一行的末尾有一个反斜杠,那么是换行折叠符号,应该和下一行合并成一起来处理,这样做主要是为了避免一行的字符太长,与C语言中的含义是一致的。注释是以#号开头。 Action和services显式声明了一个语句块,而commands和options属于最近声明的语句块。在第一个语句块之前 的commands和options会被忽略.init.rc:Android在启动过程中读取的启动脚本文件,主要完成一些初级的初始化,在/system/core/init/init.cpp中解析。rc 经常被用作程序之启动脚本的文件名。它是“run commands”(运行命令)的缩写。init.xx.rc:与具体CPU相关的启动脚本,比如对于MTK的CPU,名字为init.usb.rc,init.trace.rc。在init.rc之后得到解析。厂商的rc文件一般位于device目录2.init.rc和init.xx.rc文件的修改我们以 init.rc 来入手,学习 rc 的用法。2.1 文件结构init.rc 基本单位是 section(语句块)。一个Section以Service或On开头的语句块.以Service开头的Section叫做服务,而以On开头的叫做动作(Action).services: 服务.Action: 动作commands:命令.options:选项.trigger:触发器,或者叫做触发条件.class: 类属,即可以为多个service指定一个相同的类属,方便操作同时启动或停止.section 有三种类型:onserviceimporton 类型作表示了一组命令(commands)组成.动作包含一个触发器,决定了何时执行这个动作。当触发器的条件满足时,这个动作会被加入到已被执行的队列尾。如果此动作在队列中已经存在,那么它将不会执行. 一个动作所包含的命令将被依次执行。动作的语法如下所示:on <trigger> <command> <command> ...on 类型 表示一系列的命令组合,eg:on init # See storage config details at http://source.android.com/tech/storage/ #mkdir /storage/sdcard 0555 root root #export EXTERNAL_STORAGE /storage/sdcard # Support legacy paths #symlink /storage/sdcard /sdcard #symlink /storage/sdcard /mnt/sdcard # mkdir /tmp 01775 root root # mount tmpfs tmpfs /tmp size=25m mkdir /dev/cpuctl/bg_non_interactive chown system system /dev/cpuctl/bg_non_interactive/tasks chmod 0666 /dev/cpuctl/bg_non_interactive/tasks # KSM config write /sys/kernel/mm/ksm/pages_to_scan 100 write /sys/kernel/mm/ksm/sleep_millisecs 500 write /sys/kernel/mm/ksm/run 1 write /sys/block/zram0/comp_algorithm lz4 write /sys/block/zram0/max_comp_streams 2 # Swap in only 1 page at a time write /proc/sys/vm/page-cluster 0 这样一个 section 里面包含了多个命令。命令的执行是以 section 为单位的。上面这些命令都会一起顺序执行,不会单独执行。service 类型服务是指那些需要在系统初始化时就启动或退出时自动重启的程序.它的语法结构如下所示:service <name> <pathname> [<argument>]* <option> <option> ...service 类型 的section 表示一个可执行的程序,下面是个人工作内容中片段代码:service submcu_srv /vendor/bin/submcu_srv class core user root group root oneshotsubmcu_srv作为一个名字标识了这个 service,这个可执行程序的位置在 /vendor/bin/submcu_srv下面的oneshot 被称为 options,options 是用来描述的 service 的特点,不同的 service 有不同的 options。service 的执行总存在于某个 on 类型的section 中作为一个服务启动的动作(Action)。例如,这个submcu_srv可以在on fs中启动on fs export OMX_BELLAGIO_REGISTRY /vendor/etc/omxregister start submcu_srv例二:service yo_service1 /system/bin/yo_service1 class core user system disabled group system radio shell oneshot on yo_fs class_start core 其中 yo_service1 这个 service 的类型是 core。在 yo_fs 被调用的时候则将会 class_start 而执行所有类型为 core 的 service。options(选项)选项是用来修饰服务的。它们影响如何及何时运行这个服务.trigger(触发器)触发器用来描述一个触发条件,当这个触发条件满足时可以执行动作。commands(命令)propertiesinit程序在运行时会更新属性系统的一些属性,提供程序内部正在执行的信息.import类型import 类型表示包含了另外一些 section,在解析完 init.rc 后会继续调用 init_parse_config_file 来解析引入的 .rc 文件。(最新的android11不一定是这个函数了)eg:比如我们在 init.sc8830.rc 的开始可以看到import /init.usb.rc import /init.trace.rc表示在运行完本 rc 后还将继续运行 init.usb.rc 和 init.trace.rc。2.2 调试注意事项在默认情况下,通过init程序启动的程序的标准输出stdout和标准错误输出stderr会重定向到/dev/null.如:service akmd /system/bin/logwrapper /sbin/akmd为了更方便调试你的程序,你可以使用Android的log系统,标准输出和标准错误输出会重定义到Android的log系统中来.参考文献:init.rc介绍
2021年02月04日
1,217 阅读
0 评论
0 点赞
2021-02-04
Android.bp简介
转载自Android.bp 语法浅析-Android10.0编译系统1.概述在 Android 7.0 之前,Android 编译系统使用 GNU Make 描述和shell来构建编译规则,模块定义都使用Android.mk进行定义,Android.mk的本质就是Makefile,但是随着Android的工程越来越大,模块越来越多,Makefile组织的项目编译时间越来越长。这样下去Google工程师觉得不行,得要优化。因此,在Android7.0开始,Google采用ninja来代取代之前使用的make,由于之前的Android.mk数据实在巨大,因此Google加入了一个kati工具,用于将Android.mk转换成ninja的构建规则文件buildxxx.ninja,再使用ninja来进行构建工作。ninja的网址:https://ninja-build.org编译速度快了一些,但是既然要干, 那就干个大的,最终目标要把make都取代,于是从Android8.0开始,Google为了进一步淘汰Makefile,因此引入了Android.bp文件来替换之前的Android.mk。 Android系统的编译历程: 2.Android.bp文件格式根据设计,Android.bp 文件很简单。它们不包含任何条件语句,也不包含控制流语句;所有复杂问题都由用 Go 编写的构建逻辑处理。Android.bp 文件的语法和语义都尽可能与 Bazel BUILD 文件类似。2.1 模块Android.bp 文件中的模块以模块类型开头,后跟一组 name: "value", 格式的属性:cc_binary { name: "gzip", srcs: ["src/test/minigzip.c"], shared_libs: ["libz"], stl: "none", }每个模块都必须具有 name 属性,并且相应值在所有 name 文件中必须是唯一的,仅有两个例外情况是命名空间和预构建模块中的 Android.bp 属性值,这两个值可能会重复。srcs 属性以字符串列表的形式指定用于构建模块的源文件。您可以使用模块引用语法 ":<module-name>" 来引用生成源文件的其他模块的输出,如 genrule 或 filegroup。如需有效模块类型及其属性的列表,请参阅 Soong 模块参考:https://www.cnblogs.com/linhaostudy/p/12361659.html常用模块类型:cc_binarycc_librarycc_library_staticandroid_appjava_libraryhidl_interfaceaidl_interface2.2 类型变量和属性是强类型,变量根据第一项赋值动态变化,属性由模块类型静态设置。支持的类型为:布尔值Bool(true 或 false)整数Integers (int)字符串Strings ("string")字符串列表List of string (["string1", "string2"])映射Maps ({key1: "value1", key2: ["value2"]})映射可以包含任何类型的值,包括嵌套映射。列表和映射可能在最后一个值后面有终止逗号。2.3 Glob接受文件列表的属性(例如 srcs)也可以采用 glob 模式。glob 模式可以包含普通的 UNIX 通配符 *,例如 *.java。glob 模式还可以包含单个 ** 通配符作为路径元素,与零个或多个路径元素匹配。例如,java/**/*.java 同时匹配 java/Main.java 和 java/com/android/Main.java 模式。2.4 变量Android.bp 文件可能包含顶级变量赋值:gzip_srcs = ["src/test/minigzip.c"] cc_binary { srcs: gzip_srcs, shared_libs: ["libz"], stl: "none", }变量的作用域限定在声明它们的文件的其余部分,以及所有子 Android.bp文件,可以使用 “=” 号赋值, 但是不能使用 “:=” 赋值。变量是不可变的,但有一个例外情况:可以使用 += 赋值将变量附加到别处,但只能在引用它们之前附加。例:gzip_srcs = ["src/test/minigzip.c"] gzip_srcs += [ "src/test/test.cpp", ] cc_binary { srcs: gzip_srcs, shared_libs: ["libz"], stl: "none", }2.5 注释Android.bp 文件可以包含 C 样式的多行 / / 注释以及 C++ 样式的单行 // 注释。2.6 运算符可以使用 + 运算符附加字符串、字符串列表和映射。可以使用 + 运算符对整数求和。附加映射会生成两个映射中键的并集,并附加在两个映射中都存在的所有键的值。2.7 条件语句Soong 不支持 Android.bp 文件中的条件语句。但是,编译规则中需要条件语句的复杂问题将在 Go(在这种语言中,您可以使用高级语言功能,并且可以跟踪条件语句引入的隐式依赖项)中处理。大多数条件语句都会转换为映射属性,其中选择了映射中的某个值并将其附加到顶级属性。例如,要支持特定于架构的文件,请使用以下命令:cc_library { ... srcs: ["generic.cpp"], arch: { arm: { srcs: ["arm.cpp"], }, x86: { srcs: ["x86.cpp"], }, }, }2.8 格式设置语句Soong 包含一个针对 Blueprint 文件的规范格式设置工具,类似于 gofmt。如需以递归方式重新设置当前目录中所有 Android.bp 文件的格式,请运行以下命令:bpfmt -w .规范格式包括缩进四个空格、多元素列表的每个元素后面有换行符,以及列表和映射末尾有英文逗号。2.9 默认模块默认模块可用于在多个模块中重复使用相同的属性。例如:cc_defaults { name: "gzip_defaults", shared_libs: ["libz"], stl: "none", } cc_binary { name: "gzip", defaults: ["gzip_defaults"], srcs: ["src/test/minigzip.c"], }2.10 预编译的模块某些预构建的模块类型允许模块与其基于源代码的对应模块具有相同的名称。例如,如果已有同名的 cc_binary,也可以将 cc_prebuilt_binary 命名为 foo。这让开发者可以灵活地选择要纳入其最终产品中的版本。如果编译配置包含两个版本,则预编译模块定义中的 prefer 标记值会指示哪个版本具有优先级。请注意,某些预编译模块的名称不能以 prebuilt开头,例如 android_app_import。2.11 命名空间模块在 Android 完全从 Make 转换为 Soong 之前,Make 产品配置必须指定 PRODUCT_SOONG_NAMESPACES 值。它的值应该是一个以空格分隔的列表,其中包含 Soong 导出到 Make 以使用 m 命令进行编译的命名空间。在 Android 完成到 Soong 的转换之后,启用命名空间的详细信息可能会发生变化。Soong 可以让不同目录中的模块指定相同的名称,只要每个模块都在单独的命名空间中声明即可。可以按如下方式声明命名空间:soong_namespace { imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"], }请注意,命名空间没有 name 属性;其路径会自动指定为其名称。系统会根据每个 Soong 模块在树中的位置为其分配命名空间。每个 Soong 模块都会被视为处于 Android.bp(位于当前目录或最近的父级目录中的 soong_namespace 文件内)定义的命名空间中。如果未找到此类 soong_namespace 模块,则认为该模块位于隐式根命名空间中。下面是一个示例:Soong尝试解析由模块 M 在名称空间 N(导入命名空间 I1、I2、I3…)中声明的依赖项D。如果 D 是 //namespace:module 格式的完全限定名称,系统将仅在指定的命名空间中搜索指定的模块名称。否则,Soong 将首先查找在命名空间 N 中声明的名为 D 的模块。如果该模块不存在,Soong 会在命名空间 I1、I2、I3…中查找名为 D 的模块。最后,Soong 在根命名空间中查找。2.12 Android.mk文件 自动转Android.bp方法Android源码里边提供了快捷直接Android.mk转换成Android.bp的工具:androidmk。androidmk源码位置: build/soong/androidmk/cmd/androidmk/androidmk.go编译出来后androidmk可执行文件位置:aosp/out/soong/host/linux-x86/bin/androidmk转换方法:aosp/out/soong/host/linux-x86/bin/androidmk [Android.mk PATH] > [Android.bp PATH]该工具可以转换变量、模块、注释和一些条件,但任何自定义生成文件规则、复杂条件或额外的包含都必须手动转换。
2021年02月04日
1,735 阅读
0 评论
0 点赞
2021-02-04
Android.mk简介
Android.mk本页介绍了 ndk-build 所使用的 Android.mk 构建文件的语法。概览Android.mk 文件位于项目 jni/ 目录的子目录中,用于向构建系统描述源文件和共享库。它实际上是一个微小的 GNU makefile 片段,构建系统会将其解析一次或多次。Android.mk 文件用于定义 Application.mk、构建系统和环境变量所未定义的项目级设置。它还可替换特定模块的项目级设置。Android.mk 的语法支持将源文件分组为“模块”。模块是静态库、共享库或独立的可执行文件。您可在每个 Android.mk 文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。构建系统只将共享库放入您的应用软件包。此外,静态库可生成共享库。除了封装库之外,构建系统还可为您处理各种其他事项。例如,您无需在 Android.mk 文件中列出头文件或生成的文件之间的显式依赖关系。NDK 构建系统会自动计算这些关系。因此,您应该能够享受到未来 NDK 版本中支持的新工具链/平台功能带来的益处,而无需处理 Android.mk 文件。此文件的语法与随整个 Android 开源项目分发的 Android.mk 文件中使用的语法非常接近。虽然使用这些语法的构建系统实现并不相同,但通过有意将语法设计得相似,可使应用开发者更轻松地将源代码重复用于外部库。基础知识在详细了解语法之前,最好先了解 Android.mk 文件所含内容的基本信息。为此,本部分使用 Hello-JNI 示例中的 Android.mk 文件解释文件中每一行的作用。Android.mk 文件必须先定义 LOCAL_PATH 变量:LOCAL_PATH := $(call my-dir)此变量表示源文件在开发树中的位置。在上述命令中,构建系统提供的宏函数 my-dir 将返回当前目录(Android.mk 文件本身所在的目录)的路径。下一行声明 CLEAR_VARS 变量,其值由构建系统提供。include $(CLEAR_VARS)CLEAR_VARS 变量指向一个特殊的 GNU Makefile,后者会为您清除许多 LOCAL_XXX 变量,例如 LOCAL_MODULE、LOCAL_SRC_FILES 和 LOCAL_STATIC_LIBRARIES。请注意,GNU Makefile 不会清除 LOCAL_PATH。此变量必须保留其值,因为系统在单一 GNU Make 执行上下文(其中的所有变量都是全局变量)中解析所有构建控制文件。在描述每个模块之前,您必须声明(重新声明)此变量。接下来,LOCAL_MODULE 变量存储您要构建的模块的名称。请在应用的每个模块中使用一次此变量。LOCAL_MODULE := hello-jni每个模块名称必须唯一,且不含任何空格。构建系统在生成最终共享库文件时,会对您分配给 LOCAL_MODULE 的名称自动添加正确的前缀和后缀。例如,上述示例会生成名为 libhello-jni.so 的库。注意:如果模块名称的开头已经是 lib,构建系统不会添加额外的 lib 前缀;而是按原样采用模块名称,并添加 .so 扩展名。因此,比如原来名为 libfoo.c 的源文件仍会生成名为 libfoo.so 的共享对象文件。此行为是为了支持 Android 平台源文件根据 Android.mk 文件生成的库;所有这些库的名称都以 lib 开头。下一行会列举源文件,以空格分隔多个文件:LOCAL_SRC_FILES := hello-jni.cLOCAL_SRC_FILES 变量必须包含要构建到模块中的 C 和/或 C++ 源文件列表。最后一行帮助系统将一切连接到一起:include $(BUILD_SHARED_LIBRARY)BUILD_SHARED_LIBRARY 变量指向一个 GNU Makefile 脚本,该脚本会收集您自最近 include 以来在 LOCAL_XXX 变量中定义的所有信息。此脚本确定要构建的内容以及构建方式。在示例目录中有更为复杂的示例,包括带有注释的 Android.mk 文件供您查看。此外,示例:native-activity 详细介绍了该示例的 Android.mk 文件。最后,变量和宏提供了关于本部分中变量的更多信息。变量和宏构建系统提供许多可在 Android.mk 文件中使用的变量。其中的许多变量已预先赋值。另一些变量由您赋值。除了这些变量之外,您还可以自己定义任意变量。在定义变量时请注意,NDK 构建系统保留了下列变量名称:以 LOCAL\_ 开头的名称,例如 LOCAL_MODULE。以 PRIVATE\_、NDK\_ 或 APP 开头的名称。构建系统在内部使用这些变量名。小写名称,例如 my-dir。构建系统也是在内部使用这些变量名。如果您需要在 Android.mk 文件中定义您自己的便利变量,建议在名称前附加 MY_。NDK定义的include变量本部分探讨了构建系统在解析 Android.mk 文件前定义的 GNU Make 变量。在某些情况下,NDK 可能会多次解析 Android.mk 文件,每次使用其中某些变量的不同定义。CLEAR_VARS此变量指向的构建脚本用于取消定义下文“开发者定义的变量”部分中列出的几乎所有 LOCAL_XXX 变量。在描述新模块之前,请使用此变量来包含此脚本。使用它的语法为:include $(CLEAR_VARS)BUILD_EXECUTABLE此变量指向的构建脚本会收集您在 LOCAL_XXX 变量中提供的模块的所有相关信息,以及确定如何根据您列出的源文件构建目标可执行文件。请注意,使用此脚本要求您至少已经为 LOCAL_MODULE 和 LOCAL_SRC_FILES 赋值;如需详细了解这些变量,请参阅模块描述变量。使用此变量的语法为:include $(BUILD_EXECUTABLE)注意:大多数 Android 应用不包含可执行文件,但它们对于创建单元测试和其他调试工具很有用。BUILD_SHARED_LIBRARY此变量指向的构建脚本会收集您在 LOCAL_XXX 变量中提供的模块的所有相关信息,以及确定如何根据您列出的源文件构建目标共享库。请注意,使用此脚本要求您至少已经为 LOCAL_MODULE 和 LOCAL_SRC_FILES 赋值;如需详细了解这些变量,请参阅模块描述变量。使用此变量的语法为:include $(BUILD_SHARED_LIBRARY)共享库变量会导致构建系统生成扩展名为 .so 的库文件。BUILD_STATIC_LIBRARY用于构建静态库的 BUILD_SHARED_LIBRARY 的变体。构建系统不会将静态库复制到您的项目/软件包中,但可以使用静态库构建共享库(请参阅下文的 LOCAL_STATIC_LIBRARIES 和 LOCAL_WHOLE_STATIC_LIBRARIES)。使用此变量的语法为:include $(BUILD_STATIC_LIBRARY)静态库变量会导致构建系统生成扩展名为 .a 的库。PREBUILT_SHARED_LIBRARY指向用于指定预构建共享库的构建脚本。与 BUILD_SHARED_LIBRARY 和 BUILD_STATIC_LIBRARY 的情况不同,这里的 LOCAL_SRC_FILES 值不能是源文件,而必须是指向预构建共享库的单一路径,例如 foo/libfoo.so。使用此变量的语法为:include $(PREBUILT_SHARED_LIBRARY)您也可以使用 LOCAL_PREBUILTS 变量引用另一个模块中的预构建库。如需详细了解如何使用预构建库,请参阅使用预构建库。PREBUILT_STATIC_LIBRARY与 PREBUILT_SHARED_LIBRARY 相同,但用于预构建静态库。如需详细了解如何使用预构建库,请参阅使用预构建库。目标信息变量构建系统会根据 APP_ABI 变量所指定的每个 ABI 分别解析 Android.mk 一次,该变量通常在 Application.mk 文件中定义。如果 APP_ABI 为 all,构建系统会根据 NDK 支持的每个 ABI 分别解析 Android.mk 一次。本部分介绍构建系统每次解析 Android.mk 时定义的变量。TARGET_ARCH构建系统解析此 Android.mk 文件时指向的 CPU 系列。此变量将是下列其中一项:arm、arm64、x86 或 x86_64。TARGET_PLATFORM构建系统解析此 Android.mk 文件时指向的 Android API 级别号。例如,Android 5.1 系统映像对应于 Android API 级别 22:android-22。如需查看平台名称和相应 Android 系统映像的完整列表,请参阅原生 API。以下示例展示了使用此变量的语法:ifeq ($(TARGET_PLATFORM),android-22) # ... do something ... endifTARGET_ARCH_ABI构建系统解析此 Android.mk 文件时指向的 ABI。表 1 显示了用于每个受支持 CPU 和架构的 ABI 设置。表 1. 不同 CPU 和架构的 ABI 设置。CPU和架构设置ARMv7armeabi-v7aARMv8 AArch64arm64-v8ai686x86x86-64x86_64以下示例演示了如何检查 ARMv8 AArch64 是否为目标 CPU 与 ABI 的组合:ifeq ($(TARGET_ARCH_ABI),arm64-v8a) # ... do something ... endif如需详细了解架构 ABI 和相关兼容性问题,请参阅 Android ABI。未来的新目标 ABI 将使用不同的值。TARGET_ABI目标 Android API 级别与 ABI 的串联,特别适用于要针对实际设备测试特定目标系统映像的情况。例如,要检查在 Android API 级别 22 上运行的 64 位 ARM 设备:ifeq ($(TARGET_ABI),android-22-arm64-v8a) # ... do something ... endif模块描述变量本部分中的变量会向构建系统描述您的模块。每个模块描述都应遵守以下基本流程:使用 CLEAR_VARS 变量初始化或取消定义与模块相关的变量。为用于描述模块的变量赋值。使用 BUILD_XXX 变量设置 NDK 构建系统,使其将适当的构建脚本用于该模块。LOCAL_PATH此变量用于指定当前文件的路径。必须在 Android.mk 文件开头定义此变量。以下示例演示了如何定义此变量:LOCAL_PATH := $(call my-dir)CLEAR_VARS 所指向的脚本不会清除此变量。因此,即使 Android.mk 文件描述了多个模块,您也只需定义此变量一次。LOCAL_MODULE此变量用于存储模块名称。指定的名称在所有模块名称中必须唯一,并且不得包含任何空格。您必须先定义该名称,然后才能添加任何脚本(CLEAR_VARS 的脚本除外)。无需添加 lib 前缀或 .so 或 .a 文件扩展名;构建系统会自动执行这些修改。在整个 Android.mk 和 Application.mk 文件中,请用未经修改的名称引用模块。例如,以下行会导致生成名为 libfoo.so 的共享库模块:LOCAL_MODULE := "foo"如果您希望生成的模块使用除“lib + LOCAL_MODULE 的值”以外的名称,可以使用 LOCAL_MODULE_FILENAME 变量为生成的模块指定自己选择的名称。LOCAL_MODULE_FILENAME此可选变量使您能够替换构建系统为其生成的文件默认使用的名称。例如,如果 LOCAL_MODULE 的名称为 foo,您可以强制系统将其生成的文件命名为 libnewfoo。以下示例演示了如何完成此操作:LOCAL_MODULE := foo LOCAL_MODULE_FILENAME := libnewfoo对于共享库模块,此示例将生成一个名为 libnewfoo.so 的文件。注意:您无法替换文件路径或文件扩展名。LOCAL_SRC_FILES此变量包含构建系统生成模块时所用的源文件列表。只列出构建系统实际传递到编译器的文件,因为构建系统会自动计算所有相关的依赖项。请注意,您可以使用相对(相对于 LOCAL_PATH)和绝对文件路径。建议您避免使用绝对文件路径;相对路径可以提高 Android.mk 文件的移植性。注意:务必在构建文件中使用 Unix 样式的正斜杠 (/)。构建系统无法正确处理 Windows 样式的反斜杠 ()。LOCAL_CPP_EXTENSION可以使用此可选变量为 C++ 源文件指定 .cpp 以外的文件扩展名。例如,以下行将扩展名更改为 .cxx(设置必须包含点)。LOCAL_CPP_EXTENSION := .cxx您可以使用此变量指定多个扩展名。例如:LOCAL_CPP_EXTENSION := .cxx .cpp .ccLOCAL_CPP_FEATURES您可使用此可选变量指明您的代码依赖于特定 C++ 功能。它会在构建过程中启用正确的编译器标记和链接器标记。对于预构建的二进制文件,此变量还会声明二进制文件依赖于哪些功能,从而确保最终链接正常运行。我们建议您使用此变量,而不要直接在 LOCAL_CPPFLAGS 定义中启用 -frtti 和 -fexceptions。使用此变量可让构建系统对每个模块使用适当的标记。使用 LOCAL_CPPFLAGS 会导致编译器将所有指定的标记用于所有模块,而不管实际需求如何。例如,如需指明您的代码使用 RTTI(运行时类型信息),请写入:LOCAL_CPP_FEATURES := rtti如需指明您的代码使用 C++ 异常,请输入:LOCAL_CPP_FEATURES := exceptions您还可以为此变量指定多个值。例如:LOCAL_CPP_FEATURES := rtti features描述值的顺序无关紧要。LOCAL_C_INCLUDES您可使用此可选变量指定相对于 NDK root 目录的路径列表,以便在编译所有源文件(C、C++ 和 Assembly)时添加到 include 搜索路径中。例如:LOCAL_C_INCLUDES := sources/foo或者甚至:LOCAL_C_INCLUDES := $(LOCAL_PATH)/<subdirectory>/foo请在通过 LOCAL_CFLAGS 或 LOCAL_CPPFLAGS 设置任何对应的包含标记前定义此变量。在使用 ndk-gdb 启动原生调试时,构建系统也会自动使用 LOCAL_C_INCLUDES 路径。LOCAL_CFLAGS此可选变量用于设置在构建 C 和 C++ 源文件时构建系统要传递的编译器标记。这样,您就可以指定额外的宏定义或编译选项。可以使用 LOCAL_CPPFLAGS 仅为 C++ 指定标记。请勿尝试在 Android.mk 文件中更改优化/调试级别。构建系统可以使用 Application.mk 文件中的相关信息自动处理此设置。这样,构建系统就可以生成供调试期间使用的有用数据文件。您可通过输入以下代码指定额外的 include 路径:LOCAL_CFLAGS += -I<path>,但是,最好使用 LOCAL_C_INCLUDES,因为这样也可以使用可用于 ndk-gdb 原生调试的路径。LOCAL_CPPFLAGS只构建 C++ 源文件时将传递的一组可选编译器标记。它们将出现在编译器命令行中的 LOCAL_CFLAGS 后面。使用 LOCAL_CFLAGS 为 C 和 C++ 指定标记。LOCAL_STATIC_LIBRARIES此变量用于存储当前模块依赖的静态库模块列表。如果当前模块是共享库或可执行文件,此变量将强制这些库链接到生成的二进制文件。如果当前模块是静态库,此变量只是指出依赖于当前模块的其他模块也会依赖于列出的库。LOCAL_SHARED_LIBRARIES此变量会列出此模块在运行时依赖的共享库模块。此信息是链接时必需的信息,用于将相应的信息嵌入到生成的文件中。LOCAL_WHOLE_STATIC_LIBRARIES此变量是 LOCAL_STATIC_LIBRARIES 的变体,表示链接器应将相关的库模块视为完整归档。如需详细了解完整归档,请参阅有关 --whole-archive 标记的 GNU Id 文档。多个静态库之间存在循环依赖关系时,此变量十分有用。使用此变量构建共享库时,它将强制构建系统将静态库中的所有对象文件添加到最终二进制文件。但是,生成可执行文件时不会发生这种情况。LOCAL_LDLIBS此变量列出了在构建共享库或可执行文件时使用的额外链接器标记。利用此变量,您可使用 -l 前缀传递特定系统库的名称。例如,以下示例指示链接器生成在加载时链接到 /system/lib/libz.so 的模块:LOCAL_LDLIBS := -lz如需查看此 NDK 版本中可以链接的公开系统库列表,请参阅原生 API。注意:如果您为静态库定义此变量,构建系统会忽略此变量,并且 ndk-build 显示一则警告。LOCAL_LDFLAGS此变量列出了构建系统在构建共享库或可执行文件时使用的其他链接器标记。例如,若要在 ARM/X86 上使用 ld.bfd 链接器:LOCAL_LDFLAGS += -fuse-ld=bfd注意:如果您为静态库定义此变量,构建系统会忽略此变量,并且 ndk-build 会显示一则警告。LOCAL_ALLOW_UNDEFINED_SYMBOLS默认情况下,如果构建系统在尝试构建共享库时遇到未定义的引用,将会抛出“未定义的符号”错误。此错误可帮助您捕获源代码中的错误。如需停用此检查,请将此变量设置为 true。请注意,此设置可能会导致共享库在运行时加载。注意:如果您为静态库定义此变量,构建系统会忽略此变量,并且 ndk-build 会显示一则警告。LOCAL_ARM_MODE默认情况下,构建系统会以 thumb 模式生成 ARM 目标二进制文件,其中每条指令都是 16 位宽,并与 thumb/ 目录中的 STL 库链接。将此变量定义为 arm 会强制构建系统以 32 位 arm 模式生成模块的对象文件。以下示例演示了如何执行此操作:LOCAL_ARM_MODE := arm您也可以对源文件名附加 .arm 后缀,指示构建系统仅以 arm 模式构建特定的源文件。例如,以下示例指示构建系统始终以 ARM 模式编译 bar.c,但根据 LOCAL_ARM_MODE 的值构建 foo.c。LOCAL_SRC_FILES := foo.c bar.c.arm注意:您也可以在 Application.mk 文件中将 APP_OPTIM 设置为 debug,强制构建系统生成 ARM 二进制文件。指定 debug 会强制构建 ARM,因为工具链调试程序无法正确处理 Thumb 代码。LOCAL_ARM_NEON此变量仅在以 armeabi-v7a ABI 为目标时才有意义。它允许在 C 和 C++ 源文件中使用 ARM Advanced SIMD (NEON) 编译器内建函数,以及在 Assembly 文件中使用 NEON 指令。请注意,并非所有基于 ARMv7 的 CPU 都支持 NEON 扩展指令集。因此,必须执行运行时检测,以便在运行时安全地使用此代码。如需了解详情,请参阅 Neon 支持和 CPU 功能。此外,您也可以使用 .neon 后缀,指定构建系统仅以 NEON 支持来编译特定源文件。在以下示例中,构建系统以 Thumb 和 NEON 支持编译 foo.c,以 Thumb 支持编译 bar.c,并以 ARM 和 NEON 支持编译 zoo.c:LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon如果同时使用这两个后缀,.arm 必须在 .neon 前面。LOCAL_DISABLE_FORMAT_STRING_CHECKS默认情况下,构建系统会在编译代码时保护格式字符串。这样的话,如果 printf 样式的函数中使用了非常量格式的字符串,就会强制引发编译器错误。此保护默认启用,但您也可通过将此变量的值设置为 true 将其停用。如果没有必要的原因,我们不建议停用。LOCAL_EXPORT_CFLAGS此变量用于记录一组 C/C++ 编译器标记,这些标记将添加到通过 LOCAL_STATIC_LIBRARIES 或 LOCAL_SHARED_LIBRARIES 变量使用此模块的任何其他模块的 LOCAL_CFLAGS 定义中。例如,假设有以下模块对:foo 和 bar,它们依赖于 foo:include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo/foo.c LOCAL_EXPORT_CFLAGS := -DFOO=1 include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := bar LOCAL_SRC_FILES := bar.c LOCAL_CFLAGS := -DBAR=2 LOCAL_STATIC_LIBRARIES := foo include $(BUILD_SHARED_LIBRARY)在这里,构建系统在构建 bar.c 时会向编译器传递 -DFOO=1 和 -DBAR=2 标记。它还会在模块的 LOCAL_CFLAGS 前面加上导出的标记,以便您轻松进行替换。此外,模块之间的关系也具有传递性:如果 zoo 依赖于 bar,而后者依赖于 foo,那么 zoo 也会继承从 foo 导出的所有标记。最后,构建系统在执行局部构建时(即,构建要导出标记的模块时),不使用导出的标记。因此,在以上示例中,构建系统在构建 foo/foo.c 时不会将 -DFOO=1 传递到编译器。如需执行局部构建,请改用 LOCAL_CFLAGS。LOCAL_EXPORT_CPPFLAGS此变量与 LOCAL_EXPORT_CFLAGS 相同,但仅适用于 C++ 标记。LOCAL_EXPORT_C_INCLUDES此变量与 LOCAL_EXPORT_CFLAGS 相同,但适用于 C include 路径。例如,当 bar.c 需要包括模块 foo 的头文件时,此变量很有用。LOCAL_EXPORT_LDFLAGS此变量与 LOCAL_EXPORT_CFLAGS 相同,但适用于链接器标记。LOCAL_EXPORT_LDLIBS此变量与 LOCAL_EXPORT_CFLAGS 相同,用于指示构建系统将特定系统库的名称传递到编译器。请在您指定的每个库名称前附加 -l。请注意,构建系统会将导入的链接器标记附加到模块的 LOCAL_LDLIBS 变量值上。其原因在于 Unix 链接器的工作方式。当模块 foo 是静态库并且具有依赖于系统库的代码时,此变量通常很有用。然后,您可以使用 LOCAL_EXPORT_LDLIBS 导出依赖项。例如:include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo/foo.c LOCAL_EXPORT_LDLIBS := -llog include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := bar LOCAL_SRC_FILES := bar.c LOCAL_STATIC_LIBRARIES := foo include $(BUILD_SHARED_LIBRARY)在此示例中,构建系统在构建 libbar.so 时,将在链接器命令的末尾指定 -llog。这样就会告知链接器,由于 libbar.so 依赖于 foo,因此它也依赖于系统日志记录库。LOCAL_SHORT_COMMANDS当您的模块有很多源文件和/或依赖的静态或共享库时,请将此变量设置为 true。这样会强制构建系统将 @ 语法用于包含中间对象文件或链接库的归档。此功能在 Windows 上可能很有用,在 Windows 上,命令行最多只接受 8191 个字符,这对于复杂的项目来说可能太少。它还会影响个别源文件的编译,而且将几乎所有编译器标记都放在列表文件内。请注意,true 以外的任何值都将恢复为默认行为。您也可以在 Application.mk 文件中定义 APP_SHORT_COMMANDS,对项目中的所有模块强制实施此行为。我们不建议默认启用此功能,因为它会减慢构建速度。LOCAL_THIN_ARCHIVE构建静态库时,请将此变量设置为 true。这样会生成一个瘦归档,即一个库文件,其中不含对象文件,而只包含它通常包含的实际对象的文件路径。这对于减小构建输出的大小非常有用。但缺点是,这样的库无法移至其他位置(其中的所有路径都是相对路径)。有效值为 true、false 或空白。您可在 Application.mk 文件中通过 APP_THIN_ARCHIVE 变量来设置默认值。注意:在非静态库模块或预构建的静态库模块中,将会忽略此变量。LOCAL_FILTER_ASM请将此变量定义为一个 shell 命令,供构建系统用于过滤根据您为 LOCAL_SRC_FILES 指定的文件提取或生成的汇编文件。定义此变量会导致发生以下情况:构建系统从任何 C 或 C++ 源文件生成临时汇编文件,而不是将它们编译到对象文件中。构建系统在任何临时汇编文件以及 LOCAL_SRC_FILES 中所列任何汇编文件的 LOCAL_FILTER_ASM 中执行 shell 命令,因此会生成另一个临时汇编文件。构建系统将这些过滤的汇编文件编译到对象文件中。例如:LOCAL_SRC_FILES := foo.c bar.S LOCAL_FILTER_ASM := foo.c --1--> $OBJS_DIR/foo.S.original --2--> $OBJS_DIR/foo.S --3--> $OBJS_DIR/foo.o bar.S --2--> $OBJS_DIR/bar.S --3--> $OBJS_DIR/bar.o“1”对应于编译器,“2”对应于过滤器,“3”对应于汇编程序。过滤器必须是一个独立的 shell 命令,它接受输入文件名作为第一个参数,接受输出文件名作为第二个参数。例如:myasmfilter $OBJS_DIR/foo.S.original $OBJS_DIR/foo.S myasmfilter bar.S $OBJS_DIR/bar.SNDK 提供的函数宏本部分介绍了 NDK 提供的 GNU Make 函数宏。使用 $(call <function>) 可以对其进行求值;其返回文本信息。my-dir这个宏返回最后包括的 makefile 的路径,通常是当前 Android.mk 的目录。my-dir 可用于在 Android.mk 文件开头定义 LOCAL_PATH。例如:LOCAL_PATH := $(call my-dir)由于 GNU Make 的工作方式,这个宏实际返回的是构建系统解析构建脚本时包含的最后一个 makefile 的路径。因此,包括其他文件后就不应调用 my-dir。例如:LOCAL_PATH := $(call my-dir) # ... declare one module include $(LOCAL_PATH)/foo/`Android.mk` LOCAL_PATH := $(call my-dir) # ... declare another module这里的问题在于,对 my-dir 的第二次调用将 LOCAL_PATH 定义为 $PATH/foo,而不是 $PATH,因为这是其最近的 include 所指向的位置。在 Android.mk 文件中的任何其他内容后指定额外的 include 可避免此问题。例如:LOCAL_PATH := $(call my-dir) # ... declare one module LOCAL_PATH := $(call my-dir) # ... declare another module # extra includes at the end of the Android.mk file include $(LOCAL_PATH)/foo/Android.mk如果以这种方式构造文件不可行,请将第一个 my-dir 调用的值保存到另一个变量中。例如:MY_LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(MY_LOCAL_PATH) # ... declare one module include $(LOCAL_PATH)/foo/`Android.mk` LOCAL_PATH := $(MY_LOCAL_PATH) # ... declare another moduleall-subdir-makefiles返回位于当前 my-dir 路径所有子目录中的 Android.mk 文件列表。利用此函数,您可以为构建系统提供深度嵌套的源目录层次结构。默认情况下,NDK 只在 Android.mk 文件所在的目录中查找文件。this-makefile返回当前 makefile(构建系统从中调用函数)的路径。parent-makefile返回包含树中父 makefile 的路径(包含当前 makefile 的 makefile 的路径)。grand-parent-makefile返回包含树中祖父 makefile 的路径(包含当前父 makefile 的 makefile 的路径)。import-module此函数用于按模块名称来查找和包含模块的 Android.mk 文件。典型的示例如下所示:$(call import-module,<name>)在此示例中,构建系统在 NDK_MODULE_PATH 环境变量所引用的目录列表中查找具有 标记的模块,并且自动包括其 Android.mk 文件。google Android.mk
2021年02月04日
745 阅读
0 评论
0 点赞
2021-02-04
Make--(4)命令
make命令实质上就是一个单行shell脚本。实际上,make会获取每行命令并将它传递给subshell去执行。事实上,make会进行优化,make会通过在每个命令行中扫描shell特殊字符来进行此项检查。如果不存在任何shell特殊字符,make就会直接执行此命令,而不会将此命令传递给subshell去执行。make默认使用/bin/sh这个shell。如果需要变更,可以通过在makefile中明确设定SHELL这个变量。
2021年02月04日
985 阅读
0 评论
0 点赞
2021-02-04
Make--(3)函数
1. 用户自定义函数用户自定义函数能够将命令序列存储在变量里,让我们得以在makefile中使用各种应用程序。2.内置函数make的内置函数可分类为:字符串操作、文件名操作、流程控制、用户自定义函数以及若干重要的杂项函数。所有的函数都会具有如下形式:$(function-name arg1[, argn])$(之后是内置函数的名称,接着是函数的参数。第一个参数的前导空格会被删除,但是后续的任何参数若包含前导(当然也包括内置的和结尾的)空格则都会被保留下来。函数的参数是以逗号为分隔符,所以只有一个参数的函数并不需要使用逗号。许多只接受一个函数的参数会把它的参数视为一串以空格隔开的单词。对这些函数而言,它们会以空格作为分隔符。2.1 字符串函数1. filter函数名称:过滤函数filter格式:$(filter <pattern...>,<text> )返回:返回符合模式<pattern>的字串。功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。存在多个模式时,模式表达式之间使用空格分割。实例:sources := foo.c bar.c baz.s ugh.h foo: $(sources) cc $(filter %.c %.s,$(sources)) -o foo $(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。2. filter-out函数名称:反过滤函数filter-out格式:$(filter-out <pattern...>,<text> )返回:返回不符合模式<pattern>的字串。功能:以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以有多个模式。存在多个模式时,模式表达式之间使用空格分割。实例:objects=main1.o foo.o main2.o bar.o mains=main1.o main2.o $(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。 3. findstring函数名称:查找字符串函数格式:$(findstring <find>,<in> )返回:如果找到,那么返回<find>,否则返回空字符串。功能:在字串<in>中查找<find>字串。实例:str1 := a b c str2 := b c 第一个函数返回“a”字符串,第二个返回空字符串 all: @echo $(findstring a,$(str1)) @echo $(findstring a,$(str2))4. subst函数名称:字符串替换函数格式:$(subst <search-string>, <replace-string>, <text>)返回:函数返回被替换过后的字符串。功能:把字串<text>中的<from>字符串替换成<to>,不具备通配符能力,它最常被用来在文件名列表中将一个扩展名替换成另一个扩展名。实例:$(subst a,the,There is a big tree) 把“There is a big tree”中的“a”替换成“the”,返回结果是“There is the big tree”。5. patsubst函数名称:模式字符串替换函数格式:$(patsubst <search-pattern>,<replace-pattern>,<text> )返回:函数返回被替换过后的字符串,具备通配符能力。此处的模式只可以包含一个%字符。功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<search-pattern>,如果匹配的话,则以<replace-pattern>替换。这里,<search-pattern>可以包括通配符“%”,表示任意长度的字串。如果<replace-pattern>中也包含“%”,那么,<replace-pattern>中的这个“%”将是<search-pattern>中的那个“%”所代表的字串。(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)实例:$(patsubst %.c,%.o,x.c.c bar.c) 把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”6. words函数名称:返回单词数量格式:$(words text)返回:text中的单词数量功能:返回text中单词数量。其中,$(word n,text)返回text中的第n个单词,第一个单词的编号是1.如果n的值大于text中单词的个数,则此函数将返回空值实例:current := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))通过这种方式可以获得列表中的最后一个单词,在这里将返回最近读取的makefile的文件名。7. firstword函数名称:格式:$(firstword text)返回:功能:此函数返回text中的第一个单词。此功能等效于$(word 1,text)实例:8. wordlist函数名称:格式:$(wordlist start,end,text)返回:返回text中范围从start(含)到end(含)的单词。如果start的值大于单词的个数,返回空值。如果end的值大于单词的个数,则函数返回从start开始的所有单词。功能:实例:2.2 重要的杂项函数1. sort函数名称:sort函数格式:$(sort list)返回:返回按照字典编纂顺序排列的不重复的单词列表,并以空格作为分隔符。功能:sort函数会排序它的list参数并移除重复的项目。此外,sort函数还会删除前导以及结尾的空格。实例:2. shell函数名称:shell函数格式:$(shell <fun>)返回:shell函数的返回值。输出中所出现的一系列换行符号会被缩减成单一空格符号,任何接在后面的换行符号都会被删除。标准错误以及任何程序的结束状态都不会被返回。功能:为了使用shell的函数实例:例如files := $(shell echo *.c)2.3 文件名函数makefile的编写者通常会花许多时间在文件的处理上。1. wildcard函数名称:通配符展开函数格式:$(wildcard PATTERN...)返回:如果被扩展的模式找不到相符的文件,则会返回空字符串。功能:在Makefile规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数“wildcard”实例:一般我们可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表。复杂一些用法;可以使用“$(patsubst %.c,%.o,$(wildcard *.c))”,首先使用“wildcard”函数获取工作目录下的.c文件列表;之后将列表中所有文件名的后缀.c替换为.o。这样我们就可以得到在当前目录可生成的.o文件列表。2. dir函数名称:格式:$(dir list...)返回:功能:dir函数返回list中每个单词的目录部分。实例:3. notdir函数名称:格式:$(notdir name...)返回:功能:notdir函数返回文件路径的文件名部分。实例:4. suffix函数名称:格式:$(suffix name...)返回:功能:suffix函数会返回它的参数中每个单词的后缀(即文件扩展名)实例:5. basename函数名称:格式:$(basename name...)返回:功能:实例:6. addsuffix函数名称:格式:返回:功能:实例:7. addprefix函数名称:格式:返回:功能:实例:8. join函数名称:格式:返回:功能:实例:2.4 流程控制4. foreach函数名称:循环替换函数格式:$(foreach <var>,<list>,<text> )返回:返回循环<text>语句最终执行的结果功能:把参数<list>中的单词逐一取出放到参数<var>所指定的变量中, 然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环这个过程。实例:letters := $(foreach letter, a b c d, $(letter)) show-words: # letters has $(words $(letters)) words: `$(letters)` $ make # letters has 4 words: 'a b c d'当这个foreach函数被执行时,它会反复扩展循环主体$(letter),并且将循环控制变量letter的值依次设定成a、b、c、d。每次扩展所得到的文本会被累积起来,并以空格为分隔符。2.5 较不重要的杂项函数7. strip函数名称:去空字符函数格式:$(strip <string> )返回:返回被去掉空格的字符串值。功能:去掉<string>字串中开头和结尾的空字符,并将中间的多个连续空字符(如果有的话)合并为一个空字符实例:str1 := abc str2 := abc str3 := a b c all: @echo $(strip $(str1)) @echo $(strip $(str2)) @echo $(strip $(str3)) 输出结果: abc abc a b c 10. call函数名称:call函数是唯一一个可以创建定制化参数函数的引用函数。支持对自定义函数的引用;格式:$(call VARIABLE,PARAM,PARAM,...)返回:VARIABLE展开后的表达式的值功能:在执行时,将它的参数"PARAM"依次赋给临时变量"(1)","(2)".call对参数的数目没有限制,也可以没有参数值。最后再对VARIABLE展开后的表达式进行处理.说明:变量VARIABLE在定义时最好定义为递归展开式;call函数中对VARIABLE的调用,直接给函数或变量名就好了,不要用"$";多个PARAM使用逗号分割开,且逗号和PARAM之间不能有空格,否则会导致解析异常;实例:(1)变量的引用变量定义为直接展开式如果这里将VARIABLE1定义为直接展开式,最终调用call函数后,返回值为空我的理解是,直接展开式在定义时将(1)和(2)展开,而此时它们的值为空,所以变量的值为空.当执行到call函数时,尽管带上了需要传递的参数,但PARAM((1)和(2))之前已经被展开了,故此时已经取不到传递进来的参数值.VARIABLE1 := $(2) $(1) $(info 1-$(VARIABLE1)) aa=$(call VARIABLE1,hello,world) $(info 1-$(aa)) all: @echo Done定义为递归展开式VARIABLE1 = $(2) $(1) $(info 2-$(VARIABLE1)) aa=$(call VARIABLE1,hello,world) $(info 2-$(aa)) all: @echo Done(2)函数的引用不带参数的函数引用define FUNC1 $(info echo 3-"hello") endef $(call FUNC1) all: @echo Done 带参数的函数引用define FUNC1 $(info echo 4-$(1) $(2)) endef $(call FUNC1,hello,wolrd) all: @echo Done 1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:1. patsubst函数名称:格式:返回:功能:实例:
2021年02月04日
826 阅读
0 评论
0 点赞
2021-02-04
Make--(2)变量与宏
一个变量名称几乎可以由任何字符组成,包括大部分的标点符号。即使是空格也可以使用,但请不要这样做。事实上,只有:、\#和=等字符不允许使用在变量名称中。请注意,变量名称是区分大小写的。建议使用的命名习惯:当变量用来表示用户在命令行上或环境中所自定义的常数时,习惯上全部以大写来命名,单词之间用下划线隔开。至于只在makefile中出现的内部变量,则全部用小写来编写其名称,单词之间以下划线符号隔开。内含用户自定义函数的变量以及宏都会以小写来编写其名称,单词之间以破折号(-)隔开。1.自动变量自动变量含义$@ 工作目标的文件名$%档案文件成员(archive member)结构中的文件名元素。$<第一个必要条件的文件名$?时间戳在工作目标(的时间戳)之后的所有必要条件,并以空格隔开这些必要条件$^所有必要条件的文件名,并以空格隔开这些文件名。这份列表已删掉重复的文件名,因为对大多数的应用而言,比如编译、复制等,并不会用到重复的文件名$+ 如同$^,代表所有必要条件的文件名,并以空格隔开这些文件名。不过,$+包含重复的文件名。$*工作目标的主文件名。一个文件名称是由两部分组成:主文件名(stem)和扩展名(suffix)2. 变量的类型一般来说,以变量来代表外部程序是一个不错的注意,这让makefile的用户较容易针对他们特有的环境来改写makefile。变量可用来保存简单的常数,也可用来存放自定义的命令序列。例如下面的设定可用来汇报尚未使用的磁盘空间:DF = df AWK =awk free-space := $(DF) . | $(AWK) 'NR == 2 {print $$4}'make的变量有两种类型:经简单扩展的变量(simply expanded variable)以及经递归扩展的变量(recursively expanded variable)。经简单扩展的变量使用:=赋值运算符来定义一个经简单扩展的变量,如MAKE_DEPEND := $(CC) -M一旦make从makefile中读进该变量的定义语句,赋值运算符的右边部分会立刻被扩展。赋值运算符的右边部分只要出现make变量的引用就会被扩展,而扩展后所产生的的文本则会被存储成该变量的值。上面的变量被扩展之后就变成下面这样gcc -M然而,如果上面的CC变量尚未定义,则变量被扩展后将变成<space>-M未被定义的变量将被扩展为空值经递归扩展的变量直接使用=来定义,如MAKE_DEPEND = $(CC) -M ... # 稍后 CC = gccmake在读取变量时只会将=号右边的值赋值给变量,不会做任何的展开的动作,展开的动作会被延迟到该变量被使用的时候才进行。这样,当MAKE_DEPEND被使用的时候,即使CC并未定义,MAKE_DEPEND在脚本的值也会被扩展成gcc -M2.1 其他的赋值类型条件赋值?=此运算只会在变量的值尚不存在的状况下进行变量要求赋值的动作。附加运算符+=此运算符会将文本附加到变量里。当递归变量被使用时,赋值运算符右边部分的值会在"不影响变量中原有值的状况下"被附加到变量里。3. 宏变量适合用来存储单行形式的值,可是对于多行形式的值,例如命令脚本,如果我们想在不同的地方执行它,该怎么办?在GNU make中,我们可以通过define指令以创建“封装命令序列”(canned sequence)的方式来解决此问题,在这里简称为宏。例如,define build_target_with_subfeature_country @echo build_target_with_subfeature_country...... $(call build_target,$(filter $(TARGETS),$(subst _, ,$(1))),$(filter $(SUBFEATURE),$(subst _, ,$(1))),$(filter $(COUNTRY),$(subst _, ,$(1)))) endef define指令后面跟着变量名称以及一个换行符号。变量的主体包含了所有命令序列(每一行命令都必须前置一个Tab符号)直到ended关键字出现为止,endef关键字必须自成一行。在echo命令前置了一个@字符。当执行命令脚本时,前置@字符的命令不会被make输出。因此,当我们运行echo命令本身,只会输出该命令的输出。如果在宏内部使用@前缀,这个前缀字符只会影响使用到它的命令行。然而,如果将这个前缀字符用在宏引用上,则整个宏主体都会被隐藏起来。4. 何时扩展变量当make运行时。它会以两个阶段来完成它的工作。第一个阶段,make会读进makefile以及被引入的任何其他makefile。这个时候,其中所定义的变量和规则会被加载进make的内部数据库,而且依存图也会被建立起来。第二个阶段,make会分析依存图并且判断需要更新的工作目标,然后执行脚本以完成所需要的更新动作。当make在处理递归变量或define指令的时候,会将变量里的每一行或宏的主体存储起来,包括换行符号,但不会予以扩展。宏定义里的最后一个换行符号并不会被存储成宏的一部分;否则,宏被扩展时make会读进一个额外的换行符号。当宏被扩展时,make会立即扫描被扩展的文本中是否存在宏或变量的引用,如果存在就予以扩展,如此递归进行下去。如果宏是在命令脚本的语境中被扩展的,则宏主体的每一行都会被插入一个前导的跳格符(Tab)。下面是用来处理“makefile中的元素何时被扩展”的准则:对于变量赋值,make会在第一阶段读进该行时,立即扩展赋值运算符左边的部分。=和?=的右边部分会被延后到它们被使用的时候扩展,并且在第二阶段进行。:=的右边部分会被立即扩展如果+=的左边部分原本就被定义成一个简单变量,+=的右边部分就会被立即扩展,否则,它的求值动作会被延后。对于宏定义(使用define指令),宏的变量名称会被立即扩展,宏的主体会被延后到被使用的时候扩展对于规则,工作目标和必要条件总是会被立即扩展,然而命令总是会被延后扩展定义何时扩展a何时扩展ba = b立即延后a ?= b立即延后a := b立即立即a += b立即延后或立即define ab...endef立即延后一个通则是总是先定义变量和宏,然后再使用它们。尤其是,在工作目标或必要条件中使用变量时,就需要在使用变量之前予以定义。5. 工作目标与模式的专属变量在makefile运行期间,变量通常只有一个值。对需要经过两个处理阶段的makefile来说是这样没错。第一个阶段,make读进makefile之后,会对变量进行赋值和扩展的动作并建立依存图。第二个阶段,make会分析以及遍历依存图。所以,等到make指令命令脚本的时候,所有的变量都已经处理完毕了。但是如果我们想为特定的规则或模式重新定义变量,该怎么办?例如,现在我们想要编译一个需要额外命令行选项-DUSE_NEW_MALLOC=1的文件,但是其他的编译项目并不需要这个额外的命令行选项:gui.o: gui.h $(COMPILE.c) -DUSE_NEW_MALLOC=1 $(OUTPUT_OPTION) $<当规则有改动,或者有许多这样的文件需要处理,就会做很多重复的动作。为了解决此类问题,make提供了工作目标的专属变量。这些变量的定义会附加在工作目标之上,且只有该工作目标以及相应的任何必要条件被处理的时候,它们才会起作用。通过使用专属变量,可以把前面的例子改写为gui.o: CPPFLAGS += -DUSE_NEW_MALLOC=1 gui.o: gui.h $(COMPILE.c) $(OUTPUT_OPTION) $<工作目标的专属变量的语法如下所示:target...: variable = value target...: variable := value target...: variable += value target...: variable ?= value这类变量的赋值动作会延后到开始处理工作目标的时候进行。所以赋值运算符右边部分的值,可由另一个工作目标的专属变量来设定。同样地,此变量只有在必要条件的处理期间,才会发生作用。6. 变量来自何处文件在文件中创建,或者通过include指令引入命令行直接在make命令行上定义或重新定义变量:$ make CFLAGS=-g CPPFLAGS='-DBSD -DDEBUG'在命令行上,每个变量赋值运算符的右边部分必须是一个单独的shell参数。如果变量的值(或变量本身)包含空格,则必须为参数加上括号或是规避空格。命令行上变量的赋值结果将会覆盖掉环境变量以及makefile文件中的赋值结果。还可以使用:=或=赋值运算符将命令行参数设定成简单或递归变量。此外,如果使用override指令,你还可以要求make采用makefile的赋值结果,而不要采用命令行的赋值结果。环境make启动时,所有来自环境的变量都会被自动定义成make的变量。这些环境变量的优先级很低,所以makefile文件或命令行参数的赋值结果将会覆盖掉环境变量的值。不过,可以使用--environment-overrides或-e命令行选项,让环境变量覆盖掉相应的makefile变量。当make被递归调用时,有若干来自上层的make变量会通过环境传递给下层的make。默认情况下,只有原先就来自环境的变量会被导出到下层的环境之中。不过,你只要使用export指令就可以让任何变量被导出到环境之中:要求将所有变量全部导出,可以这么做:export请注意,即使这些变量的名称包含了无效的shell变量字符,make也会进行导出的动作。使用unexport可以避免环境变量被导出到子进程条件赋值运算符与环境变量的交互良好。假如你已经在makefile中定义了输出目录,但是你希望用户能轻易地改写,使用条件赋值运算符将会是最佳解决方案:# 假设输出目录为$(PROJECT_DIR)/out OUTPUT_DIR ?= $(PROJECT_DIR)/out使用下面的较冗长的方式同样可以实现同样的效果ifndef OUTPUT_DIR # 假设输出目录为$(PROJECT_DIR)/out OUTPUT_DIR = $(PROJECT_DIR)/out endif其中的差别在于,如果变量的值已经设定,那么即使是空值,条件赋值运算符也会跳过赋值的动作,而运算符ifdef和ifndef只会测试“非空值”。因此,我们会使用条件运算符而不会使用ifdef来对OUTPUT_DIR赋值。不建议过多的使用环境变量自动创建最后,make会在执行一个规则的命令脚本之前立刻创建自动变量。7. 条件指令条件指令的基本语法如下所示:if-condition text if the condition is true endif或:if-condition text if the condition is true else text if the condition is false endif其中,if-condition可以是以下之一:ifdef variable-name ifndef variable-name ifeq test ifneq test进行ifdef/ifndef的测试时,不应该以$()括住variable-name。最后,test可以表示成下面这样 "a" "b" 或 (a,b)其中,单引号或双引号可以交替使用(但是引号必须成对出现)。条件处理指令可用在宏定义和命令脚本中,还可以放在makefile的顶层:libGui.a: $(gui_objects) $(AR) $(ARFLAGS) $@ $< ifdef RANLIB $ (RANLIB) $@ endif我喜欢缩排我的条件指令,但是草率的缩排动作可能会导致错误。在前面的例子中,条件指令被缩排了四个空格,而且其所括住的命令具有一个前导的tab符号。如果其所括住的命令并非以一个tab符开头,make将不会把它视为命令;如果条件指令具有一个前导的tab符,make会误以为"条件指令"就是"命令"而将之传递给subshell。ifeq和ifneq条件指令可用来测试其参数是否相等。条件指令里空格的处理有些微妙。举例来说,如果参数采用小括号的形式,那么逗号之后的空格将会被忽略,除此之外所有其他的空格都是有意义的:ifeq (a, a) # These are equal endif ifeq ( b, b ) # So are these endif我比较喜欢使用等效的引号形式:ifeq "a" "a" # These are equal endif ifeq 'b' 'b' # So are these endif即使如此,还是经常会发生"变量扩展后包含了非预期的空格符号"的状况。这可能引发一些问题,因为进行匹配时会将所有字符纳入考虑。为了创建更稳定的makefile,我们会使用strip函数。ifeq "$(strip $(OPTIONS))" "d" COMPILATION_FLAGS += -DDEBUG endif8. include指令用法include xxx.mk引入文件与依存关系当make看到include指令时,会事先对通配符以及变量引用进行扩展的动作,然后试着读进include的文件。如果这个文件存在,则整个处理过程会继续下去;然而,如果这个文件不存在,则make会汇报问题并且继续读取其余的makefile。当所有的读取动作皆已完成之后,make会从规则数据库中找出任何可用来更新引入文件的规则。如果找到了一个相符的规则,make就会按照正常的步骤来更新工作目标。如果任何一个引入文件被规则更新,make接着会清楚它的内部数据库并且重新读进整个makefile。如果完成读取、更新和重新读取的过程之后,仍有include指令因为文件不存在而执行失败,那么make就会显示错误状态并终止执行。如果想让make忽略无法加载的引入文件,可以为include指令前置一个破折号-include xxx.mk 9. 标准的make变量除了自动变量,make还会为“自己的状态以及内置规则的定义”提供变量,以便对外提供相关信息:MAKE_VERSIONGNU make的版本号CURDIR正在执行make进程的当前工作目录。此变量的值将会是shell变量PWD的值,除非make在运行时用到了--directory(或-C)选项。--directory选项会使得make在搜索任何makefile之前变更到不同的目录。这个选项的完整形式为--directory=directory-name或-C directory-name。这样,CURDIR将会包含--include-dir的目录参数。在makefile文件中,所有路径都应该被设定成相对于makefile所在的目录。需要使用绝对路径时可以通过CURDIR进行访问。MAKEFILE_LISTmake所读进的各个makefile文件的名称所构成的列表,包括默认的makefile以及命令行或include指令所指定的makefile。在每个makefile被读进make之前,其文件名会被附加到MAKEFILE_LIST变量里。所以,任何一个总是可以查看此列表的最后一项来判断自己的文件名。MAKECMDGOALS对当前运行的make而言,make运行时命令行上指定了哪些工作目标。此变量并不包含命令行选项或变量的赋值。.VARIABLES到目前为止,make从各个makefile文件所读进的变量的名称所构成的列表,不含工作目标的专属变量。此变量仅供读取,对它所进行的任何赋值动作都会被忽略掉。
2021年02月04日
758 阅读
0 评论
0 点赞
2021-02-04
Make--(1)规则
makefile中的工作目标依存于一组必要条件,这些条件一般也都是文件。当你要求更新某个工作目标时,如果必要条件中存在时间戳在工作目标的时间戳之后的文件,make就会执行相应规则里的命令脚本。因为某个规则的工作目标可以是另一个规则的必要条件,所以这样的工作目标和必要条件将会形成依存图(dependency graph)。建立及处理依存图,并据此更新特定的工作目标,就是make所要做的事。规则对make而言十分重要,make允许你使用各种类型的规则。
2021年02月04日
821 阅读
0 评论
0 点赞
1
...
31
32
33
...
37