CMake、CMakeLists.txt 基础语法

前言

代码变成可执行文件,叫做编译(compile);先编译这个,还是先编译那个(即编译的安排),叫做构建(build)。CMake是最常用的构建工具,诞生于1977年,主要用于C语言的项目。但是实际上 ,任何只要某个文件有变化,就要重新构建的项目,都可以用CMake构建

cmake是一个根据指定的Shell命令进行构建的工具。它的规则很简单,你规定要构建哪个文件、它依赖哪些源文件,当那些文件有变动时,如何重新构建它。

cmake 构建命令如下

cmake ..       //或者cmake.这里两个点表示在上层文件夹下寻找cmakelists生成makefile和相关文件
make           //执行makefile
make install   //(optional)安装make clean     // 清理工程

1、创建项目

括号中的字符不需要用双引号括起来

project(name)

2、set() 添加、修改变量

添加变量和修改变量用的同一个指令;

# 添加常亮
set(NAME_1 yexindong)# 添加字符串变量
set(NAME_2 "yexindong")# 添加数值变量
set(NAME_3 15)# 设置变量result1的值为"abc",并将该变量的作用域扩展至父目录。PARENT_SCOPE 变量的作用域升级到父目录,即在当前目录的父目录中也可以使用该变量
set(result1 "abc" PARENT_SCOPE)# 设置全局变量,设置一个名为EXAMPLE_VAR的全局变量,并将其值设置为example_value。INTERNAL 变量定义只在当前的CMakeLists.txt文件中有效,不会传递给子目录
set(EXAMPLE_VAR "example_value" CACHE INTERNAL "")

2.1 清除变量 unset()

unset会清除变量内的值

unset(var CACHE)

3、常用系统变量

// 常用系统变量
${PROJECT_SOURCE_DIR}     // 指向项目路径,也就是src路径,如果没有就是项目路径
${PROJECT_BINSARY_DIR}    // 指向编译路径,也就是build文件夹,如果没有则默认跟项目路径相同set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINSARY_DIR}/bin)   // 修改常用系统变量路径EXECUTABLE_OUTPUT_PATH,也就是可执行文件输出路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)       // 修改常用系统变量路径LIBRARY_OUTPUT_PATH,也就是库文件输出路径
set(CMAKE_INSTALL_PREFFIX=/usr)                          // 修改cmake安装的默认前缀路径,默认是/usr/local,也就是默认是针对本用户/usr/local,可以改为针对所有用户/usr 

4、打印信息

# 打印字符串
message(STATUS "yes")# 打印变量的值
message(STATUS ${name_1})

5、add_difinitions() 增加编译选项

如果要增加c++11的相关特性,比如nullptr,则增加该句,放在add_executable()前面:

add_definitions(-std=c++11)

6、指定子src目录

如果要单独一个src子文件夹放置.cpp和.h文件,则需要在根路径的cmakelists增加这句,同时src文件夹下增加额外cmakelists文件。
参考Teris源码。此时,主目录下的cmakelists主要用来包含src文件夹,并没有生成可执行文件,而src里边的cmakelists则会有生成可执行文件。

add_subdirectory(src)

7、include_directories() 添加头文件路径

有两个语句都可以实现添加头文件路径,一个是include_directories()这是需要放在生成可执行文件之前。
另一个是target_include_directories()这是需要放在在生成可执行文件之后。

include_directories(./common)target_include_directories(main ./common)

8、link_libraries() 添加.so动态库文件路径

实测发现默认的.so路径只有一个/usr/lib,也就是库文件如果在这个路径下则不需要欧手动链接。
但如果不是在这个路径下,或者是在这个路径下的文件夹中,则需要手动链接,即添加

link_libraries(“lib_path.so”)
8.1 动态库文件路径第一种方式

有两个语句都可以实现链接库文件,一个是link_libraries(path)这是在生成可执行文件之前就指定库文件路径(必须放在add_executable()的前边)。
另一个语句也可以实现链接库文件,即用target_link_libraries(main path)这是在生成target可执行文件之后链接(必须放在add_executable()的后边)
两者的区别就是在定义链接文件时实在可执行文件生成之前还是之后,都可以实现功能

8.2 动态库文件路径第二种方式

另外一种方法实现链接库文件:就是把该库文件所在路径加入到CMAKE_INCLUDE_PATH中然后用find_path()指令。
但注意:加入到CMAKE_INCLUDE_PATH并不代表他会把路径提供给编译器,还是需要自己用find_path找到该头文件。
这种方式的优点在于,只要加入到路径后,所有find_指令都可以使用

export CMAKE_INCLUDE_PATH=/usr/local/include/test   
find_path(myHeader hello.h)         // 把头文件找到,并赋值给变量myHeader
include_directories(${myHeader})    // 把该路径包含进来
8.3 动态库文件路径第三种方式

第三种方法实现链接库文件:就是利用find_package()来查找cmake支持的模块,或者自定义的模块来获得对应头文件和库文件路径。
这种方法参考find_package()的使用。
find_library()和find_path()查找库文件和头文件
查找库文件需提供库文件名,用find_library(var name HINTS path PATH_SUFFIXES suff1 suff2), 即搜索名称为name的库文件(实际名称是libname.so),找到后存入var中,
并可以带多个关键参数,其中HINTS关键参数代表搜索路径,PATH_SUFFIXES代表路径后缀

find_library(_NVINFER_LIB nvinfer HINTS ${TRT_LIB} PATH_SUFFIXES lib lib64)

9、find_path 查找文件所在的目录

CMake中的命令find_path用于查找指定的文件的目录,如果指定的文件在目录中找到,则结果将存储在 var中,除非清除var,否则不会重复搜索。如果没找到,结果将为var-NOTFOUND。

unset(var CACHE) # 清除变量
find_path(var head.hpp) # 查找文件所在的目录
message(STATUS ${var})

打印结果

/var/data/   # 若找到,返回文件所在的目录
var-NOTFOUND  # 若未找到,返回的信息

10、find_package() 查找第三方库的头文件和链接库文件路径

注意:采用find_package()命令cmake的模块查找顺序是:先在变量${CMAKE_MODULE_PATH}查找,然后在/usr/shared/cmake/Modules/里边查找。

find_package(CUDA REQUIRED)           # 查找某个第三方库的cmake module,比如CUDA代表的就是FindCUDA.cmake这个module
find_package(OpenCV REQUIRED)         # 多个库分别查找, 然后统一加到include_directories和link_libraries即可 
target_include_directories(tensorrt PUBLIC ${CUDA_INCLUDE_DIRS} ${TENSORRT_INCLUDE_DIR})
target_link_libraries(tensorrt ${CUDA_LIBRARIES} ${TENSORRT_LIBRARY} ${CUDA_CUBLAS_LIBRARIES} ${CUDA_cudart_static_LIBRARY} ${OpenCV_LIBS})

11、file()对文件和文件夹的操作

如果要自动搜索所有支持文件,则可以考虑自动搜索命令

file(GLOB Sources *.cpp) # 从当前目录查询所有的.cpp文件 ,将结果赋值到 Sources 变量中
file(GLOB Includes *.h) # 从当前目录查询所有的.h 文件,将结果赋值到 Includes 变量中
add_executable(Cars ${Sources} ${Includes})  # 生成可执行文件# 以下2行代码执行的结果是一样的
file (GLOB files *.cpp */*.cpp)  # 从当前目录和子目录查询所有的.cpp文件 ,将结果赋值到 files 变量中
file(GLOB_RECURSE files *.c) # 添加当前目录及其子目录下的所有c文件列表到files变量中# 搜索指定目录以及子目录中所有的以.cpp结尾的文件,然后把它们保存到  native_srcs 变量中
file(GLOB_RECURSE native_srcs src/main/cpp/*.cpp)
11.1 创建文件夹
 # 创建某个路径文件夹,MAKE_DIRECTORY 是固定的,path是你需要创建的文件夹的全路径地址
file(MAKE_DIRECTORY path) 

12、add_library() 生成动态或者静态链接库

可以选择生成SHARED动态库, STATIC静态库.
注意:库名称不需要写前缀lib,系统会自动在给出的库名称前面加lib.

// 创建动态库
add_library(algorithms SHARED ${src_path})  // 创建动态链接库:库名称libalgorithms, SHARED表示为.so动态链接库,src_path是.cpp文件所在路径
// 创建静态库
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)         // 设置不清除同名动态库hello.so
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)  // 设置不清除同名静态库hello.a
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")    // 设置静态库的输出名称为hello,从而即使跟动态库重名也能实现

13、add_executable() 生成可执行文件

第一个参数生成后的文件名称,
第二个参数是需要生成的源文件路径,一般都是 .c、 .cpp 或者.h的文件

add_executable(file_name path)

14、install() 安装生成的头文件和库文件:

cmake install 是 CMake 的一个命令,用于安装构建的项目到指定的目录中。

在项目的 CMakeLists.txt 文件中,你可以使用 make install 命令来指定要安装的文件和目录。install 命令的语法如下:

install(TARGETS <target1> <target2> ...DESTINATION <destination>[COMPONENT <component>][PERMISSIONS permissions...][CONFIGURATIONS [Debug|Release|...]][OPTIONAL]
)

其中,TARGETS 参数用于指定要安装的目标(可执行文件、库文件等),DESTINATION 参数用于指定安装目标的目录。

例如,假设你的项目生成了一个可执行文件 myapp,你可以使用以下命令将它安装到 /usr/local/bin 目录中:

install(TARGETS myappDESTINATION /usr/local/bin
)

你还可以使用 install 命令安装其他类型的文件,比如库文件、头文件、配置文件等。例如,要安装一个库文件和相应的头文件,可以使用以下命令:

install(TARGETS mylibDESTINATION /usr/local/lib
)
install(FILES mylib.hDESTINATION /usr/local/include
)

在构建项目时,可以使用make install命令来执行安装操作。这将把构建的目标文件复制到指定的安装目录中。

请注意,安装目录可能需要管理员权限才能写入。如果没有足够的权限,你可能需要使用 sudo make install 命令来以管理员身份执行安装。

15、function() 函数

# 定义函数,函数名为 getDir,后面的param1和param2都是参数
function(get_dir param1 param2)message(STATUS "123-" ${param1} "-" ${param2})
endfunction()# 调用函数,两个参数之间用 空格隔开
get_dir(1  2)

16、STRING() 用法

16.1、 FIND 在字符串中查询子子字符串是否存在

在CMake中,可以使用字符串操作函数来判断一个字符串中是否包含另一个字符串。以下是一些常用的函数:
在中查找,如果找到则将其位置保存在<output_variable>中,否则将其赋值为-1。

STRING(FIND <string> <substring> <output_variable>)

示例

 # 判断字符串是否包含 cmake-build-debug ,将结果输出到indexOfStr变量,若包含返回 > -1的值,若不包含返回-1
STRING(FIND ${dir} cmake-build-debug indexOfStr)
16.2、 SUBSTRING 字符串截取

截取字符串str从索引start开始的长度为length的子字符串。

string(SUBSTRING str start length)

示例

# 截取字符串从索引1开始的长度为3的子字符串
string(SUBSTRING "hello world" 1 3 result)
message(STATUS "result: ${result}") # 输出 "ell"
16.3、 LENGTH 获取字符串长度
#获取字符串str的长度为len。
string(LENGTH str len)# 获取字符串的长度
string(LENGTH "hello world" len)
message(STATUS "len: ${len}") # 输出 "11"
16.4、 TOLOWER 将字符串str转换为小写字母。
# 将字符串str转换为小写字母。
string(TOLOWER str)# 将字符串转换为小写字母
string(TOLOWER "HELLO WORLD" result)
message(STATUS "result: ${result}") # 输出 "hello world"
16.5、TOUPPER 将字符串str转换为大写字母。
# 将字符串str转换为大写字母。
string(TOUPPER str)# 将字符串转换为大写字母
string(TOUPPER "hello world" result)
message(STATUS "result: ${result}") # 输出 "HELLO WORLD"
16.6、REPLACE:替换字符串中的子字符串
string(REPLACE "world" "CMake" my_replaced_string "hello world")
message("Replaced string is ${my_replaced_string}")# 输出:Replaced string is hello CMake
16.7、COMPARE:比较两个字符串
string(COMPARE EQUAL "hello" "hello" my_equal)
message("Strings are equal: ${my_equal}")
string(COMPARE EQUAL "hello" "world" my_equal)
message("Strings are equal: ${my_equal}")# 输出:
Strings are equal: TRUE
Strings are equal: FALSE
16.8、MATCH:匹配正则表达式
string(MATCH "hello" "hello world" my_match)
message("Matched string is ${my_match}")
# 输出:
Matched string is hello
16.9、REGEX:正则表达式替换
string(REGEX REPLACE "world" "CMake" my_replaced_string "hello world")
message("Replaced string is ${my_replaced_string}")
# 输出:
Replaced string is hello CMake
16.10、ASCII:获取字符的ASCII码值
string(ASCII "a" my_ascii_value)
message("ASCII value is ${my_ascii_value}")
# 输出:
ASCII value is 97
16.11、CONFIGURE:在字符串中进行配置
set(my_string "Hello ${CMAKE_PROJECT_NAME}!")
string(CONFIGURE "${my_string}" my_configured_string)
message("Configured string is ${my_configured_string}")# 输出:
Configured string is Hello <project-name>!
其中,<project-name>会被替换为实际的项目名称

17、while 循环

CMake 中 while 循环的语法为:

while(<condition>)# 循环体# ...
endwhile()

是循环条件,可以是任何返回布尔值(即 true 或 false)的表达式或变量。当条件成立时,循环体会被执行,循环体可以包含任意 CMake 命令或函数。循环体执行完后,继续检查循环条件,如果条件仍然成立,则重复执行循环体,直到循环条件不成立。

下面是一些 while 循环的用法示例:

例子 1
set(i 0)
while(i LESS 5)message("i = ${i}")math(EXPR i "${i} + 1")
endwhile()

输出:

i = 0
i = 1
i = 2
i = 3
i = 4
例子 2
set(j 10)
while(j GREATER_EQUAL 0)message("j = ${j}")math(EXPR j "${j} - 2")
endwhile()

输出:

j = 10
j = 8
j = 6
j = 4
j = 2
j = 0
例子 3
set(k 3)
while(k)message("Hello, world!")math(EXPR k "${k} - 1")
endwhile()

输出:

Hello, world!
Hello, world!
Hello, world!
例子 4
set(str "abc")
while(str MATCHES "a.*")message("${str}")string(REGEX REPLACE "^a" "" str "${str}")
endwhile()

输出:

abc
bc
c

18、aux_source_directory 添加指定目录中的源文件到变量

aux_source_directory 是一个 CMake 命令,用于将指定目录中的源文件自动添加到变量中。这个命令的语法如下:

aux_source_directory(<dir> <variable>)

其中:

  • <dir> 是要搜索源文件的目录名称,
  • <variable> 是一个变量,用于存储找到的源文件列表。

注意.c.cpp 才是源文件,.h结尾的头文件不是源文件

aux_source_directory 命令会自动搜索指定目录下的所有源文件,并将它们的完整路径添加到 变量中。这个命令会自动为每个源文件生成一个相对路径,并将它们存储在 变量中。

aux_source_directory 命令通常用于自动发现源文件,特别是在构建一个大型项目时,手动列出每个源文件可能会很繁琐和容易出错。通过使用 aux_source_directory 命令,可以更方便地管理源文件列表。

以下是一个示例,演示了如何使用 aux_source_directory 命令:

# 搜索 src 目录下的所有源文件,并将它们存储在 SOURCES 变量中
aux_source_directory(src SOURCES)# 将 SOURCES 变量添加到一个可执行目标中
add_executable(myapp ${SOURCES})

在上面的示例中,aux_source_directory 命令会搜索 src 目录下的所有源文件,并将它们的完整路径存储在 SOURCES 变量中。然后,SOURCES 变量被添加到 add_executable 命令中,作为构建可执行目标 myapp 的源文件列表。

需要注意的是,aux_source_directory 命令只会在第一次调用时将源文件添加到变量中,后续对同一目录调用该命令不会再次添加源文件。如果你在构建过程中添加了新的源文件,需要重新运行 CMake 来更新源文件列表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91482.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91482.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《文明5》错误代码0xc0000142修复方法

只要是错误代码为0xc0000142&#xff1f;不管是哪种错误&#xff0c;都是一样的。 修复方法有很多&#xff0c;我先推荐个人认为比较好用的修复方法 方式一&#xff1a;第三方软件修复&#xff1a; 地址在这里获取&#xff1a;修复软件点这里 添加图片注释&#xff0c;不超过 …

【Java面试题】缓存穿透

什么是缓存穿透 缓存穿透是指当秒杀请求在Redis中未命中缓存时&#xff0c;系统会转而查询数据库。若数据库中也不存在该数据&#xff0c;大量此类请求将直接冲击数据库&#xff0c;造成数据库负载激增。解决方案 缓存空值 当我们查询数据库发现数据库当中也不存在该数据时&…

SpringBoot与Rust实战指南

基于Spring Boot和Rust的实用 以下是基于Spring Boot和Rust的实用示例,涵盖常见开发场景,分为Spring Boot(Java)和Rust两部分: Spring Boot 示例 RESTful API 开发 @RestController @RequestMapping("/api") public class UserController {@GetMapping("…

【世纪龙科技】汽车整车维护仿真教学软件-智构整车维护实训

在职业院校汽车专业实训教学中&#xff0c;"设备损耗大、操作风险高、场景覆盖有限"三大痛点长期制约着教学质量提升——传统实训车间里&#xff0c;学生接触实车的机会受限于车辆台套数与维护周期&#xff0c;复杂工位流程难以反复演练&#xff1b;高危操作环节&…

CMake set_source_files_properties使用解析

set_source_files_properties() 是 CMake 中用于精细化控制源文件属性的多功能命令。除了设置编译标志外&#xff0c;它还有许多其他重要用途。以下是全面的用法解析&#xff1a;一、核心功能分类 1. 编译控制 编译器选项&#xff1a;COMPILE_FLAGS / COMPILE_OPTIONSset_sourc…

雷达微多普勒特征代表运动中“事物”的运动部件。

雷达微多普勒特征代表运动中“事物”的运动部件。 即使一个人在椅子上来回摇晃&#xff0c;肉眼看来这个动作也很简单。但对雷达来说&#xff0c;这是微动作的丰富混合&#xff1a;移动膝盖和腿、摆动手臂&#xff0c;甚至是倾斜的椅子。所有这些都会产生独特但复杂的微多普勒特…

FreeRTOS硬件中断发生时的现场

在FreeRTOS中&#xff0c;当硬件中断发生时&#xff0c;当前正在运行的任务会立即被挂起&#xff0c;处理器会跳转到中断相关的中断服务程序中&#xff0c;在中断服务程序执行期间&#xff0c;遵循以下规则&#xff1a;1、中断独占CPU&#xff0c;ISR拥有最高的执行优先级&…

kotlin语法和特性分析

核心设计哲学&#xff1a; 简洁 (Concise): 减少样板代码&#xff08;如 getter/setter、类型推导&#xff09;&#xff0c;让代码表达更直接。安全 (Safe): 从语言层面设计来避免常见错误&#xff08;尤其是空指针异常&#xff09;。互操作性 (Interoperable): 与 Java 无缝集…

二进制数本身没有默认的有符号或无符号解释

文章目录1. ​**​硬件层面&#xff1a;CPU 不区分有符号/无符号​**​2. ​**​解释权在程序员手中​**​3. ​**​默认倾向性&#xff08;非绝对规则&#xff09;​**​4. ​**​如何避免混淆&#xff1f;​**​5. ​**​经典示例​**​总结1. **解释为无符号数&#xff08;U…

(AI) Server (Hardware) Architecture

Overview by Atlas T800 Just found a good product demo. from Huawei for its Atlas T800, here 计算产品3D展示 First turn off all modules and we can delve into how this server is organized. Core This is an AI server with 910B as its main feature, which is …

【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博评论数据可视化分析-用户评论词云图实现

大家好&#xff0c;我是java1234_小锋老师&#xff0c;最近写了一套【NLP舆情分析】基于python微博舆情分析可视化系统(flaskpandasecharts)视频教程&#xff0c;持续更新中&#xff0c;计划月底更新完&#xff0c;感谢支持。今天讲解微博评论数据可视化分析-用户评论词云图实现…

【Linux学习|黑马笔记|Day1】Linux初识、安装VMware Workstation、安装CentOS7、远程连接、虚拟机快照

Linux DAY1 前言 因为之前MySQL学到安装Linux版本的MySQL了&#xff0c;需要安装虚拟机等等&#xff0c;所以我打算先学完Linux的全部课程&#xff0c;期间继续学MySQL 文章目录Linux DAY1一.1&#xff09;操作系统概述2&#xff09;Linux初识3&#xff09;虚拟机4.1&#xff…

编程与数学 03-002 计算机网络 13_无线网络技术

编程与数学 03-002 计算机网络 13_无线网络技术一、无线网络的基本概念&#xff08;一&#xff09;无线通信的频段与标准&#xff08;二&#xff09;无线网络的优势与挑战二、无线局域网&#xff08;WLAN&#xff09;&#xff08;一&#xff09;802.11标准系列&#xff08;二&a…

肖特基二极管MBR0540T1G 安森美ON 低电压 高频率 集成电路IC 芯片

MBR0540T1G ON Semiconductor&#xff1a;超低VF肖特基二极管&#xff0c;重新定义电源效率&#xff01;&#x1f525; 一、产品简介 MBR0540T1G 是安森美&#xff08;ON Semiconductor&#xff09;推出的0.5A/40V肖特基势垒二极管&#xff0c;采用专利沟槽结构&#xff0c;专…

windows内核研究(软件调试-调试事件采集)

软件调试调试事件采集前面有说到在调试器和被调试之间会创建一个_DEBUG_OBJECT对象来进行关联调试事件的种类 被调试进程会把一个个的调试事件写到_DEBUG_OBJECT中的一个成员链表中&#xff0c;调试器就通过它们建立的 _DEBUG_OBJECT调试对象获取调式事件&#xff0c;但并不是进…

Web开发-PHP应用组件框架前端模版渲染三方插件富文本编辑器CVE审计

类别组件/框架说明[Web框架]Laravel现代化、功能全面的框架&#xff0c;适合大多数Web应用。Symfony高度模块化、功能强大的框架&#xff0c;适合复杂应用。CodeIgniter轻量级框架&#xff0c;适合快速开发。Zend Framework (Laminas)企业级框架&#xff0c;适合大规模应用&…

Spring Boot Actuator 保姆级教程

1. 引言 Spring Boot Actuator 是一个功能强大的监控工具&#xff0c;能够帮助开发者监控和管理应用的运行状态。通过 Actuator&#xff0c;我们可以轻松获取应用的健康状况、配置信息、性能指标等。本文将一步步引导你如何配置和使用 Actuator&#xff0c;以及如何通过它来监控…

使用 whisper, 音频分割, 初步尝试,切割为小块,效果还不错 1

对于一首歌而言,如何断句?即,一个 mp4 或是 mp3 文件,或是一段录音, 如何使用程序,或是 ai 来断句。分割为一句一句的片段??如果人工来分割,一般是使用 capcut 之类的剪辑软件。但是效率太慢了。所以我想能否设计一个简洁的,自动的程序来处理。这种事情,专业的名称…

AD2S1210的DOS LOT含义

一、​​信号质量监控类寄存器​​​​LOT阈值&#xff08;0x88&#xff09;​​​​作用​​&#xff1a;设定信号丢失&#xff08;Loss of Signal&#xff09;的判定门槛。​​场景​​&#xff1a;当正弦或余弦输入信号幅值低于此值时&#xff0c;芯片认为信号丢失&#xff…

Au速成班-多轨编辑流程

基础编辑工作流&#xff0c;包含文件导入&#xff0c;导出&#xff0c;音量调节&#xff0c;部分效果添加。 创建多轨会话 设置工程文件名称、文件位置、采样率、位深度、主控等。 界面管理 &#xff0c;界面说明详细可看 Au速成班-基础篇_au界面介绍-CSDN博客 音量调节点击…