Android构建系统:Android.mk(1)基础概念赋值变量引用详解

简介: Android构建系统:Android.mk(1)基础概念赋值变量引用详解

在这篇博客中将探讨学习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.somyapp文件

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文件中还有很多其他的变量和函数,有时间会继续测试补充。

相关文章
|
3天前
|
移动开发 调度 Android开发
构建高效Android应用:Kotlin协程的实践指南
【5月更文挑战第30天】在移动开发领域,Android平台的流畅性与效率一直是开发者追求的核心。随着Kotlin语言的普及,其提供的协程特性为编写高效、轻量级的异步代码提供了强大工具。本文将深入探讨如何在Android项目中利用Kotlin协程来优化性能,提升用户体验。我们将从协程的基本概念出发,通过实例演示如何在实际开发中合理运用协程,并讨论协程对资源管理和错误处理的影响。
13 3
|
3天前
|
移动开发 安全 Android开发
构建高效Android应用:Kotlin协程的实践与优化策略
【5月更文挑战第30天】 在移动开发领域,性能优化始终是关键议题之一。特别是对于Android开发者来说,如何在保证应用流畅性的同时,提升代码的执行效率,已成为不断探索的主题。近年来,Kotlin语言凭借其简洁、安全和实用的特性,在Android开发中得到了广泛的应用。其中,Kotlin协程作为一种新的并发处理机制,为编写异步、非阻塞性的代码提供了强大工具。本文将深入探讨Kotlin协程在Android开发中的应用实践,以及如何通过协程优化应用性能,帮助开发者构建更高效的Android应用。
|
3天前
|
移动开发 数据库 Android开发
构建高效Android应用:Kotlin协程的实践指南
【5月更文挑战第30天】 在移动开发领域,性能优化和流畅的用户体验是至关重要的因素。对于Android开发者来说,Kotlin协程作为一种异步编程解决方案,提供了强大且轻量级的机制来处理后台任务,而不会对主线程造成阻塞。本文将深入探讨Kotlin协程的概念、优势以及如何在Android应用中实现它们,从而改善应用响应性和用户满意度。通过实例代码和最佳实践的分享,我们将展示如何有效利用协程来处理网络请求、数据库操作和耗时计算,同时确保UI的流畅性。
|
2天前
|
存储 安全 Android开发
构建高效的Android应用:Kotlin与Jetpack的结合
【5月更文挑战第31天】 在移动开发的世界中,Android 平台因其开放性和广泛的用户基础而备受开发者青睐。随着技术的进步和用户需求的不断升级,开发一个高效、流畅且易于维护的 Android 应用变得愈发重要。本文将探讨如何通过结合现代编程语言 Kotlin 和 Android Jetpack 组件来提升 Android 应用的性能和可维护性。我们将深入分析 Kotlin 语言的优势,探索 Jetpack 组件的核心功能,并通过实例演示如何在实际项目中应用这些技术。
|
2天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【5月更文挑战第31天】在现代Android开发中,异步编程和性能优化成为关键要素。Kotlin协程作为一种在JVM上实现轻量级线程的方式,为开发者提供了简洁而强大的并发处理工具。本文深入探讨了如何在Android项目中利用Kotlin协程提升应用的响应性和效率,包括协程的基本概念、结构以及实际运用场景,旨在帮助开发者通过具体实例理解并掌握协程技术,从而构建更加流畅和高效的Android应用。
|
2天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的全面指南
【5月更文挑战第31天】 在移动开发领域,性能优化和流畅的用户体验是至关重要的。随着Kotlin语言在Android开发中的普及,其提供的协程功能已成为简化异步编程、提高应用响应性和效率的强大工具。本文将深入探讨Kotlin协程的概念、优势以及如何在Android应用中实现它们。通过实际案例分析,我们将展示如何利用协程提升数据处理能力,同时保持UI线程不被阻塞,确保用户界面流畅无阻。
|
2天前
|
JSON Android开发 开发者
构建高效Android应用:采用Kotlin协程优化网络请求
【5月更文挑战第31天】 在移动开发领域,尤其是针对Android平台,网络请求的管理和性能优化一直是开发者关注的焦点。随着Kotlin语言的普及,其提供的协程特性为异步编程提供了全新的解决方案。本文将深入探讨如何利用Kotlin协程来优化Android应用中的网络请求,从而提升应用的响应速度和用户体验。我们将通过具体实例分析协程与传统异步处理方式的差异,并展示如何在现有项目中集成协程进行网络请求优化。
|
3天前
|
物联网 区块链 Android开发
构建高效Android应用:Kotlin与Jetpack的实践之路未来技术的融合潮流:区块链、物联网与虚拟现实的交汇点
【5月更文挑战第30天】 在移动开发领域,效率和性能始终是开发者追求的核心。随着技术的不断进步,Kotlin语言以其简洁性和现代化特性成为Android开发的新宠。与此同时,Jetpack组件为应用开发提供了一套经过实践检验的库、工具和指南,旨在简化复杂任务并帮助提高应用质量。本文将深入探索如何通过Kotlin结合Jetpack组件来构建一个既高效又稳定的Android应用,并分享在此过程中的最佳实践和常见陷阱。
|
3天前
|
安全 物联网 测试技术
构建未来:Android与IoT设备的无缝交互深入探索软件自动化测试的未来趋势
【5月更文挑战第30天】在物联网(IoT)技术快速发展的当下,Android系统因其开放性和广泛的用户基础成为了连接智能设备的首选平台。本文将探讨如何通过现代Android开发技术实现智能手机与IoT设备的高效、稳定连接,并分析其中的挑战和解决方案。我们将深入挖掘Android系统的底层通信机制,提出创新的交互模式,并通过实例演示如何在Android应用中集成IoT控制功能,旨在为开发者提供一套可行的指导方案,促进IoT生态系统的进一步发展。
|
3天前
|
移动开发 数据库 Android开发
构建高效Android应用:Kotlin协程的全面应用指南
【5月更文挑战第30天】在移动开发领域,性能优化和流畅的用户体验始终是开发者追求的目标。随着Kotlin语言在Android平台的广泛采用,其提供的协程特性成为了实现这一目标的有力工具。本文将深入探讨如何在Android应用中使用Kotlin协程进行异步编程,提升应用的响应性和稳定性,同时保证代码的简洁性和可维护性。