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

相关文章
|
2天前
|
移动开发 API Android开发
构建高效Android应用:Kotlin协程的实践指南
【5月更文挑战第11天】 在移动开发领域,性能优化和资源管理是至关重要的。特别地,对于Android开发者来说,合理利用Kotlin协程可以极大地改善应用的响应性和稳定性。本文将深入探讨Kotlin协程在Android中的实际应用,包括它们如何简化异步编程模型、提高UI线程的响应性,以及减少内存消耗。我们将通过具体案例分析,了解如何在实际项目中有效地使用协程,从而帮助开发者构建更加高效的Android应用程序。
|
2天前
|
Android开发
Android 如何将定制的Launcher成为系统中唯一的Launcher
Android 如何将定制的Launcher成为系统中唯一的Launcher
19 2
|
1天前
|
设计模式 缓存 Java
补齐Android技能树——从AGP构建过程到APK打包过程,安卓rxjava面试
补齐Android技能树——从AGP构建过程到APK打包过程,安卓rxjava面试
|
1天前
|
移动开发 监控 Android开发
构建高效安卓应用:Kotlin 协程的实践与优化
【5月更文挑战第16天】 在移动开发领域,性能优化一直是开发者们追求的重要目标。特别是对于安卓平台来说,由于设备多样性和系统资源的限制,如何提升应用的响应性和流畅度成为了一个关键议题。近年来,Kotlin 语言因其简洁、安全和高效的特点,在安卓开发中得到了广泛的应用。其中,Kotlin 协程作为一种轻量级的并发解决方案,为异步编程提供了强大支持,成为提升安卓应用性能的有效手段。本文将深入探讨 Kotlin 协程在安卓开发中的应用实践,以及通过合理设计和使用协程来优化应用性能的策略。
16 8
|
1天前
|
Android开发
Android获取当前系统日期和时间的三种方法
Android获取当前系统日期和时间的三种方法
15 4
|
1天前
|
安全 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【5月更文挑战第16天】 在移动开发领域,性能一直是开发者关注的焦点。随着Kotlin语言的普及,其与Java在Android应用中的性能表现成为热门话题。本文将深入分析Kotlin和Java在Android平台上的性能差异,并通过实际测试数据来揭示二者在编译速度、应用启动时间以及运行效率方面的表现。我们的目标是为开发者提供一个参考依据,以便在选择合适的编程语言时做出更加明智的决策。
|
1天前
|
存储 缓存 Android开发
Android系统分区与升级
Android系统分区与升级
21 4
|
2天前
|
安全 搜索推荐 物联网
构建未来:基于Android的智能物联网家居系统
【5月更文挑战第15天】 在快速发展的数字化时代,智能物联网(IoT)技术与移动操作系统的结合正在塑造未来家居的生活方式。特别是Android平台,以其开放性、灵活性和广泛的用户基础,成为智能家居创新的理想选择。本文将探讨如何利用Android系统构建一个高效、安全且易于扩展的智能家居控制系统,涵盖系统设计、关键技术实现以及可能面临的挑战。通过分析具体案例,我们旨在为开发者和企业提供一套可行的解决方案蓝图,以促进智能家居领域的进一步发展。
|
2天前
|
移动开发 测试技术 Android开发
构建高效Android应用:从优化用户体验到提升性能表现
【5月更文挑战第15天】 在移动开发领域,一个成功的Android应用不仅需要具备吸引用户的功能,更应提供流畅和高效的用户体验。随着技术的不断进步,开发者面临着将先进技术集成到现有架构中以提高应用性能的挑战。本文将深入探讨如何通过最新的Android框架和工具来优化应用性能,包括对UI的响应性、内存管理以及多线程处理等关键方面的改进,旨在帮助开发者构建出更加强大、快速且稳定的Android应用。
|
2天前
|
缓存 Android开发 UED
构建高效Android应用:从优化用户体验到提升性能
【5月更文挑战第15天】 在移动开发领域,构建一个高效的Android应用不仅仅意味着实现功能,还要确保流畅的用户体验和出色的性能。本文将深入探讨如何通过界面优化、代码整洁、资源管理和多线程处理等技术手段来提升Android应用的整体效率。我们将透过实际案例,揭示常见性能瓶颈的成因,并提供相应的解决方案。此外,文章还会涵盖最新的Android Studio工具和Lint检查的使用,帮助开发者早期发现潜在问题。