Android build system: Android.mk(1) basic concept assignment variable reference detailed explanation

In this blog, we will discuss how to use assignment operations, variable references and functions in the Android.mk file, as well as examples of static libraries and executable files. By using these concepts, you can more flexibly control the variables and expressions in the Android.mk file to achieve customer-satisfied functionality.

Android build system: Android.mk(1) basic concept assignment variable reference detailed explanation
Android build system: Android.mk(2) function detailed explanation
Android build system: Detailed explanation of Android.mk(3) condition control

Study reference:

Android.mk | Android NDK | Android Developers - Android developers.

1. What is Android.mk?

Android.mk is a file used to describe the build process of an Android project. It is an extension based on Makefile syntax. The role and importance of Android.mk lies in:

  • You can define which source files, resource files, library files, etc. are included in the project, as well as the dependencies between them.
  • You can specify the project's compilation options, link options, preprocessor definitions, etc., as well as adaptation to different platforms and architectures.
  • You can control the output type of the project, such as executable files, static libraries, dynamic libraries, APKs, etc., as well as their installation location and permissions.
  • Some built-in functions and variables can be called to implement some complex logic and functions.

2. Basic composition of Android.mk

A typical Android.mk file consists of the following parts:

  • Comments: Lines starting with # are comments and will not be parsed.
  • Blank lines: Blank lines will be ignored and will not affect parsing.
  • Assignment operation: The assignment operation is used to assign a value to a variable. There are a variety of assignment operators to choose from.
  • Variable reference: Variable reference is used to obtain the value of a variable. There are multiple reference methods to choose from.
  • Function: A function is a special variable that can accept parameters and return results. There are a variety of built-in functions and custom functions available.
  • Conditional statement: Conditional statements are used to perform different operations based on conditions. There are a variety of conditional judgment symbols to choose from.
  • Control statement: Control statement is used to control the execution flow of the code block. There are a variety of control keywords to choose from.
  • Print operation: The print operation is used to output information on the terminal. There are a variety of printing functions available.
  • Module definition: The module definition is the most important part of the Android.mk file. It is used to define one or more building modules and specify their properties and behavior.

This Android.mkl series will introduce the specific content and usage of these parts one by one. Please read later.

3. Assignment operation

The assignment operation is one of the most common basic operations in the Android.mk file. It is used to assign a value to a variable or change the value of an existing variable. In the Makefile syntax, there are four assignment operators to choose from, they are :=, +=, ?= and a>=. Their meaning and usage are as follows:

  • :=: This assignment operator represents immediate assignment, that is, when the assignment statement is parsed, the value of the expression on the right is calculated and assigned to the variable on the left. For example:
# 使用:= 赋值操作符
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
  • +=: This assignment operator represents append assignment, that is, adding the value of the expression on the right after the original value of the variable and assigning it to the variable on the left. For example:
# 使用:= 赋值操作符
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
  • ?=: This assignment operator represents conditional assignment, that is, the value after the equal sign is assigned only when the variable has not been assigned. If the variable has already been assigned a value, the assignment here will not take effect. For example:
# 使用:= 赋值操作符
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 已经被赋值过了

  • =: This assignment operator represents delayed assignment, that is, the value of the variable is expanded only when the variable is used. This means that if the value of a variable is later changed, the places where the variable was previously used will also change. For example:
# 使用= 延迟赋值操作符
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. Variable reference

Variable reference is used to obtain the value of a variable. There are two reference methods to choose from:

  • $(变量名): This method means getting the value of the variable and replacing it at the reference location. For example:
# 使用:= 赋值操作符
VAR := hello1
# 使用$(变量名) 引用变量的值
$(warning VAR is $(VAR)) # 输出 VAR is hello1
  • ${变量名}: This method is equivalent to the previous method, but uses different brackets. For example:
# 使用:= 赋值操作符
VAR := hello2
# 使用${变量名} 引用变量的值
$(warning VAR is ${VAR}) # 输出 VAR is hello2

Variable references have many practical applications in Android.mk files, such as:

  • Define the library file list: You can use a variable to store the library file list, and then reference the variable in the module definition. For example:
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)

Corresponding source code:

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();

  • Define the source file list: You can use a variable to store the source file list and then reference the variable in the module definition. For example:
# 构建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)

Corresponding source code:

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. Testing and Code

Complete directory structure:

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

Insert image description here
Android.mk Complete code:

$(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 Seal

[ 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: ====== 测试结束 ======

Generation concept libmylib.soJapanese myapptext item

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

Push the file to the development board Android system and prepare for testing

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可执行程order
Insert image description here

Summarize

This article introduces the use of assignment operations, variable references and functions in the Android.mk file, as well as examples of static libraries and executable files. By using these concepts, you can more flexibly control the variables and expressions in the Android.mk file to achieve customer-satisfied functionality. There are many other variables and functions in the Android.mk file, and I will continue to test and add them when I have time.

Guess you like

Origin blog.csdn.net/SHH_1064994894/article/details/134073429