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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一歲抬頭

点赞1元,收藏免费,打赏随意。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值