在这篇博客中将探讨学习Android.mk文件中赋值操作、变量引用和函数的使用方法,静态库和可执行文件的示例。通过使用这些概念,可以更灵活地控制Android.mk文件中的变量和表达式,从而实现满足客户的功能。
Android构建系统:Android.mk(1)基础概念赋值变量引用详解
Android构建系统:Android.mk(2)函数详解
Android构建系统:Android.mk(3)条件控制详解
学习参考:
Android.mk | Android NDK | Android Developers - Android 开发者.
1. 什么是Android.mk?
Android.mk 是一个用于描述Android项目构建过程的文件,它是基于Makefile语法的一种扩展。Android.mk 的作用与重要性在于:
- 可以定义项目中包含哪些源文件、资源文件、库文件等,以及它们之间的依赖关系。
- 可以指定项目的编译选项、链接选项、预处理器定义等,以及不同平台和架构的适配。
- 可以控制项目的输出类型,如可执行文件、静态库、动态库、APK等,以及它们的安装位置和权限。
- 可以调用一些内置的函数和变量,以实现一些复杂的逻辑和功能。
2. Android.mk 的基本组成
一个典型的Android.mk 文件由以下几个部分组成:
- 注释:以
#
开头的行是注释,不会被解析。 - 空白行:空白行会被忽略,不影响解析。
- 赋值操作:赋值操作用于给变量赋值,有多种赋值操作符可供选择。
- 变量引用:变量引用用于获取变量的值,有多种引用方法可供选择。
- 函数:函数是一种特殊的变量,它可以接受参数并返回结果,有多种内置函数和自定义函数可供使用。
- 条件语句:条件语句用于根据条件执行不同的操作,有多种条件判断符号可供选择。
- 控制语句:控制语句用于控制代码块的执行流程,有多种控制关键字可供选择。
- 打印操作:打印操作用于在终端输出信息,有多种打印函数可供使用。
- 模块定义:模块定义是Android.mk 文件中最重要的部分,它用于定义一个或多个构建模块,并指定它们的属性和行为。
这个Android.mkl系列将逐一介绍这些部分的具体内容和用法 请往后看。
3. 赋值操作
赋值操作是Android.mk文件中最常见的基础操作之一,它用于给变量赋予一个值,或者改变一个已有变量的值。在Makefile语法中,有四种赋值操作符可供选择,它们分别是:=
、+=
、?=
和=
。它们的含义和用法如下:
:=
:这个赋值操作符表示立即赋值,即在赋值语句被解析时就计算右边表达式的值,并将其赋给左边变量。例如:
# 使用:= 赋值操作符
file_name := source_file
# 打印file_name的值
$(warning file_name is $(file_name)) # 输出 file_name is source_file
# 改变file_name的值
file_name := header_file
# 再次打印file_name的值
$(warning file_name is $(file_name)) # 输出 file_name is header_file
+=
:这个赋值操作符表示追加赋值,即在变量原有的值后面添加右边表达式的值,并将其赋给左边变量。例如:
# 使用:= 赋值操作符
file_extension := .txt
# 打印file_extension的值
$(warning file_extension is $(file_extension)) # 输出 file_extension is .txt
# 使用+= 追加赋值操作符
file_extension += .bak
# 再次打印file_extension的值
$(warning file_extension is $(file_extension)) # 输出 file_extension is .txt.bak
?=
:这个赋值操作符表示条件赋值,即只有在变量没有被赋值过时才赋予等号后面的值。如果变量已经被赋值过,那么这里的赋值将不会生效。例如:
# 使用:= 赋值操作符
project_name := MyProject
# 打印project_name的值
$(warning project_name is $(project_name)) # 输出 project_name is MyProject
# 使用?= 条件赋值操作符
project_name ?= NewProject
# 再次打印project_name的值
$(warning project_name is $(project_name)) # 输出 project_name is MyProject # 注意这里的结果没有改变,因为 project_name 已经被赋值过了
=
:这个赋值操作符表示延迟赋值,即在使用变量时才展开变量的值。这意味着如果变量的值在之后被改变了,那么之前使用这个变量的地方也会随之改变。例如:
# 使用= 延迟赋值操作符
output_dir = build
# 打印output_dir的值
$(warning output_dir is $(output_dir)) # 输出 output_dir is build
# 改变output_dir的值
output_dir = dist # 注意这里不是使用 := ,而是使用 =
# 再次打印output_dir的值
$(warning output_dir is $(output_dir)) # 输出 output_dir is dist
# 使用:= 赋值操作符
config := debug
# 使用$(变量名) 引用变量的值
$(warning config is $(config)) # 输出 config is debug
# 使用:= 赋值操作符
config := release
# 使用${变量名} 引用变量的值
$(warning config is ${config}) # 输出 config is release
4. 变量引用
变量引用用于获取变量的值,有两种引用方法可供选择:
$(变量名)
:这种方法表示获取变量的值,并替换到引用位置。例如:
# 使用:= 赋值操作符
VAR := hello1
# 使用$(变量名) 引用变量的值
$(warning VAR is $(VAR)) # 输出 VAR is hello1
${变量名}
:这种方法与上一种方法等价,只是使用了不同的括号。例如:
# 使用:= 赋值操作符
VAR := hello2
# 使用${变量名} 引用变量的值
$(warning VAR is ${VAR}) # 输出 VAR is hello2
变量引用在Android.mk 文件中有很多实际应用,例如:
- 定义库文件列表:可以使用变量来存储库文件列表,然后在模块定义中引用该变量。例如:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 定义源文件列表
LOCAL_SRC_FILES_APP := \
src/main.cpp \
src/foo.cpp \
src/bar.cpp
# 定义库文件列表
LOCAL_SHARED_LIBRARIES := libmylib
# 构建libmylib动态库
include $(CLEAR_VARS)
LOCAL_MODULE := libmylib
LOCAL_SRC_FILES := libsrc/libmylib.cpp
include $(BUILD_SHARED_LIBRARY)
对应的源码:
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example01/libsrc$ cat libmylib.cpp
#include <iostream>
void libFunction() {
std::cout << "This is from libmylib." << std::endl;
}
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example01/libsrc$ cat libmylib.h
#pragma once
void libFunction();
- 定义源文件列表:可以使用变量来存储源文件列表,然后在模块定义中引用该变量。例如:
# 构建myapp应用程序
include $(CLEAR_VARS)
LOCAL_MODULE := myapp
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES_APP)
LOCAL_SHARED_LIBRARIES := libmylib
LOCAL_LDLIBS := -llog
LOCAL_CFLAGS := -Wall -Werror
include $(BUILD_EXECUTABLE)
对应的源码:
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example01/src$ cat bar.cpp
#include "bar.h"
#include <iostream>
#include "../libsrc/libmylib.h" // 添加这一行
void barFunction() {
libFunction();
std::cout << "Inside barFunction()" << std::endl;
// TODO: Implement barFunction
}
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example01/src$ cat bar.h
#ifndef BAR_H
#define BAR_H
void barFunction();
#endif // BAR_H
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example01/src$ cat foo.cpp
#include "foo.h"
#include <iostream>
void fooFunction() {
std::cout << "Inside fooFunction()" << std::endl;
// TODO: Implement fooFunction
}
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example01/src$ cat foo.h
#ifndef FOO_H
#define FOO_H
void fooFunction();
#endif // FOO_H
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example01/src$ cat main.cpp
#include "foo.h"
#include "bar.h"
#include <iostream>
int main() {
std::cout << "Starting main()" << std::endl;
fooFunction();
barFunction();
std::cout << "Ending main()" << std::endl;
return 0;
}
5. 测试和代码
完整的目录结构:
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk/vendor/customize/demo/example01$ tree
.
├── Android.mk
├── libsrc
│ ├── libmylib.cpp
│ └── libmylib.h
└── src
├── bar.cpp
├── bar.h
├── foo.cpp
├── foo.h
└── main.cpp
2 directories, 8 files
Android.mk
完整代码:
$(warning ====== 测试开始 ======)
# 使用:= 赋值操作符
file_name := source_file
# 打印file_name的值
$(warning file_name is $(file_name)) # 输出 file_name is source_file
# 改变file_name的值
file_name := header_file
# 再次打印file_name的值
$(warning file_name is $(file_name)) # 输出 file_name is header_file
# 使用:= 赋值操作符
file_extension := .txt
# 打印file_extension的值
$(warning file_extension is $(file_extension)) # 输出 file_extension is .txt
# 使用+= 追加赋值操作符
file_extension += .bak
# 再次打印file_extension的值
$(warning file_extension is $(file_extension)) # 输出 file_extension is .txt.bak
# 使用:= 赋值操作符
project_name := MyProject
# 打印project_name的值
$(warning project_name is $(project_name)) # 输出 project_name is MyProject
# 使用?= 条件赋值操作符
project_name ?= NewProject
# 再次打印project_name的值
$(warning project_name is $(project_name)) # 输出 project_name is MyProject # 注意这里的结果没有改变,因为 project_name 已经被赋值过了
# 使用= 延迟赋值操作符
output_dir = build
# 打印output_dir的值
$(warning output_dir is $(output_dir)) # 输出 output_dir is build
# 改变output_dir的值
output_dir = dist # 注意这里不是使用 := ,而是使用 =
# 再次打印output_dir的值
$(warning output_dir is $(output_dir)) # 输出 output_dir is dist
# 使用:= 赋值操作符
config := debug
# 使用$(变量名) 引用变量的值
$(warning config is $(config)) # 输出 config is debug
# 使用:= 赋值操作符
config := release
# 使用${变量名} 引用变量的值
$(warning config is ${config}) # 输出 config is release
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 定义源文件列表
LOCAL_SRC_FILES := \
src/main.cpp \
src/foo.cpp \
src/bar.cpp
# 定义一个可执行模块
LOCAL_MODULE := myapp
# 明确指定这是一个可执行文件
LOCAL_MODULE_CLASS := EXECUTABLES
# 编译选项
LOCAL_CFLAGS := -Wall -Werror
# 链接选项
LOCAL_LDLIBS := -llog
# 引用源文件列表变量
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES)
include $(BUILD_EXECUTABLE)
$(warning ====== 测试结束 ======)
all:
@echo "测试example01结束!"
执行mmm vendor/customize/demo/example01
打印
[ 79% 221/278] including vendor/customize/demo/example01/Android.mk ...
vendor/customize/demo/example01/Android.mk:1: warning: ====== 测试开始 ======
vendor/customize/demo/example01/Android.mk:6: warning: file_name is source_file
vendor/customize/demo/example01/Android.mk:10: warning: file_name is header_file
vendor/customize/demo/example01/Android.mk:15: warning: file_extension is .txt
vendor/customize/demo/example01/Android.mk:19: warning: file_extension is .txt .bak
vendor/customize/demo/example01/Android.mk:24: warning: project_name is MyProject
vendor/customize/demo/example01/Android.mk:28: warning: project_name is MyProject
vendor/customize/demo/example01/Android.mk:33: warning: output_dir is build
vendor/customize/demo/example01/Android.mk:37: warning: output_dir is dist
vendor/customize/demo/example01/Android.mk:42: warning: config is debug
vendor/customize/demo/example01/Android.mk:47: warning: config is release
vendor/customize/demo/example01/Android.mk:52: warning: VAR is hello1
vendor/customize/demo/example01/Android.mk:57: warning: VAR is hello2
vendor/customize/demo/example01/Android.mk:87: warning: ====== 测试结束 ======
生成对应的libmylib.so
和myapp
文件
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk$ cd out/target/product/rk3568_s$ find -name "libmylib*"
./obj_arm/SHARED_LIBRARIES/libmylib_intermediates
./obj_arm/SHARED_LIBRARIES/libmylib_intermediates/LINKED/libmylib.so
./obj_arm/SHARED_LIBRARIES/libmylib_intermediates/libsrc/libmylib.d
./obj_arm/SHARED_LIBRARIES/libmylib_intermediates/libsrc/libmylib.o
./obj_arm/SHARED_LIBRARIES/libmylib_intermediates/libmylib.so.strip.d
./obj_arm/SHARED_LIBRARIES/libmylib_intermediates/libmylib.so
./obj_arm/SHARED_LIBRARIES/libmylib_intermediates/libmylib.so.toc
./symbols/system/lib/libmylib.so
./symbols/system/lib64/libmylib.so
./system/lib/libmylib.so
./system/lib64/libmylib.so
./obj/SHARED_LIBRARIES/libmylib_intermediates
./obj/SHARED_LIBRARIES/libmylib_intermediates/LINKED/libmylib.so
./obj/SHARED_LIBRARIES/libmylib_intermediates/libsrc/libmylib.d
./obj/SHARED_LIBRARIES/libmylib_intermediates/libsrc/libmylib.o
./obj/SHARED_LIBRARIES/libmylib_intermediates/libmylib.so.strip.d
./obj/SHARED_LIBRARIES/libmylib_intermediates/libmylib.so
./obj/SHARED_LIBRARIES/libmylib_intermediates/libmylib.so.toc
ln28@ln28-pc:~/sourcecode/rk_android12.0_sdk$ cd out/target/product/rk3568_s$ find -name "myapp"
./symbols/system/bin/myapp
./system/bin/myapp
./obj/EXECUTABLES/myapp_intermediates/myapp
./obj/EXECUTABLES/myapp_intermediates/LINKED/myapp
推送文件到开发板Android系统中,准备测试
C:\Users\Administrator>adb root
adbd is already running as root
C:\Users\Administrator>adb remount
remount succeeded
C:\Users\Administrator>adb push "Z:\rk_android12.0_sdk\out\target\product\rk3568_s\system\bin\myapp" /system/bin/
615 KB/s (11720 bytes in 0.018s)
C:\Users\Administrator>adb push "Z:\rk_android12.0_sdk\out\target\product\rk3568_s\system\lib\libmylib.so" /system/lib/
703 KB/s (5080 bytes in 0.007s)
C:\Users\Administrator>adb push "Z:\rk_android12.0_sdk\out\target\product\rk3568_s\system\lib64\libmylib.so" /system/lib
64/
1071 KB/s (11376 bytes in 0.010s)
执行myapp
可执行程序
总结
本文介绍了Android.mk文件中赋值操作、变量引用和函数的使用方法,静态库和可执行文件的示例。通过使用这些概念,可以更灵活地控制Android.mk文件中的变量和表达式,从而实现满足客户的功能。Android.mk文件中还有很多其他的变量和函数,有时间会继续测试补充。