CMake 架构和用法
CMake 架构
整个 SoC 项目的 CMakeLists.txt 目录结构和调用关系如下图所示:
层次划分:
SOC 项目 :每个 SOC 项目根目录都包含一个
CMakeLists.txt作为编译入口,其下所有 MCU 项目都是在这里添加的。对应amebaxxx_gcc_project/CMakeLists.txt。MCU 项目 :每个 MCU 项目使用统一的工具链和配置参数(个别配置会因固件或 ROM 有差异),不同 MCU 项目可能使用不同的工具链,MCU 项目下又包括了一个或多个固件的编译。对应
amebaxxx_gcc_project/project_xxx/CMakeLists.txt。固件 :主要包括
image1,image2,image3,每个固件将在编译完成后生成对应的 axf 文件,axf 文件经过处理后生成最终可供烧录的 bin 文件。三者区别如下:image1: 定义了 MCU 系统中引导加载程序(bootloader)和系统闪存布局的通用基础架构,并提供了一个安全引导加载程序,支持便捷的软件升级。对应amebaxxx_gcc_project/project_xxx/asdk/make/image1。image2: 是该 SoC 的主应用固件,通常包含实时操作系统(FreeRTOS)和应用程序任务。对应amebaxxx_gcc_project/project_xxx/asdk|vsdk/make/image2/CMakeLists.txt。image3: 具有高度可配置性,允许用户仅包含所需的安全服务与功能。它受到读保护(RDP)的保护,将在安全引导加载程序中解密,并被加载至由 TrustZone 技术保护的安全静态随机存储器(SRAM)中。对应amebaxxx_gcc_project/project_xxx/asdk/make/image3/CMakeLists.txt。
组件 :位于和 SOC 项目同级的
component目录下,按照不同的功能划分为不同的子目录,每个组件都有相应的CMakeLists.txt定义其编译及被链接等配置。例如component/wifi/CMakeLists.txt。固件中添加了所需要的多个组件。
注意
在上图中, image1,image2,image3 目录中的 CMakeLists.txt 中可以 为各固件单独添加所需的组件、定义编译规则,它们是相互独立的。
组件 CMakeLists.txt 的构成
用于编译组件的 CMakeLists.txt 由 public 部分 和 private 部分 两部分组成:
前者描述了该组件向 全局编译配置 中追加的内容
后者描述了将该组件编译成 库文件 的编译配置
public 部分
该部分用于向 全局编译配置 中 追加 如下编译配置:
头文件路径:如果其他组件要使用当前组件的头文件,则可以将当前组件头文件目录加入全局。
预定义:如果当前组件被添加编译时,需要同步在全局加入某些预定义(使用场景较少)。
链接库:如果 当前组件给下游客户时不提供源码,只提供库文件,则链接时需要链接库文件而不是 target,所以需要将库文件路径加入全局以便链接时取出。
上述三类配置在如下代码块中分别由 public_includes,public_definitions,public_libraries 三个变量设置,用户可以在如下高亮代码行区域内对其进行更新。
可以参考模板 cmake/CMakeLists-template.cmake 中的一些示例,相关 API 可参考 list 操作,具体示例可以参考 修改现有组件的编译配置。
set(public_includes) #public include directories, NOTE: relative path is OK
set(public_definitions) #public definitions
set(public_libraries) #public libraries(files), NOTE: linked with whole-archive options
#------------------------------------------------------------------#
# Component public part, user config begin(DO NOT remove this line)
# set public_includes, public_definitions, public_libraries in this section
# Component public part, user config end(DO NOT remove this line)
#------------------------------------------------------------------#
ameba_global_include(${public_includes})
ameba_global_define(${public_defines})
ameba_global_library(${public_libraries}) #default: whole-achived
备注
如果当前组件没有必要对上述三类配置进行更新,上述高亮代码部分可以留空。
private 部分
该部分描述了该组件库文件的编译配置,主要包括:
源文件:当前组件编译的源文件
头文件路径:仅对当前组件有效
预定义:仅对当前组件有效
编译选项:仅对当前组件有效
上述四类配置在如下代码块中分别由 private_sources,private_includes ,
private_definitions,private_compile_options 四个变量设置,用户可以在高亮代码行区域内对其进行更新。
可以参考模板 cmake/CMakeLists-template.cmake 中的一些用法,相关 API 可参考 list 操作。
注意
组件最终的编译配置(特别是 头文件路径 , 预定义 及 编译选项)是由 private 部分 和 public 部分 组成,头文件路径和编译选项前者优先级高于后者,例如组件实际头文件搜索路径包括:
当前组件 private 部分添加的头文件路径。
全局编译配置 中的头文件路径, 包含 当前组件 的和 其他组件 追加的。
所以在 public 部分 已经添加的编译配置就无需在 private 部分 重复添加。
一般情况下建议:
倾向于将头文件路径放在 private 部分,以避免头文件污染。
除非是比较通用或底层组件,会被很多其他组件用到,此时放在 public 部分 可以提高复用性。
set(private_sources) #private source files, NOTE: relative path is OK
set(private_includes) #private include directories, NOTE: relative path is OK
set(private_definitions) #private definitions
set(private_compile_options) #private compile_options
#------------------------------#
# Component private part, user config begin
# set private_sources, private_includes, private_definitions, private_compile_options in this section
# Component private part, user config end
#------------------------------#
ameba_add_internal_library(foo
p_SOURCES
${private_sources}
p_INCLUDES
${private_includes}
p_DEFINITIONS
${private_definitions}
p_COMPILE_OPTIONS
${private_compile_options}
)
备注
上述高亮代码部分修改可参考 修改现有组件的编译配置。
如果组件已经有独立编译脚本的目录,则参考 适配具备独立编译系统的代码。
快速指南
修改现有组件的编译配置
可以参考 CMakeLists.txt 的结构 相关说明,也可以使用 一些可用于复杂逻辑处理的判断类型 ,并且 list 操作 API 可以多次调用。
以下是一些具体的例子:
添加 public 头文件路径,建议使用相对路径:
ameba_list_append(public_includes ${CMAKE_CURRENT_SOURCE_DIR} # Access current dir by CMAKE_CURRENT_SOURCE_DIR, same as . ${CMAKE_CURRENT_SOURCE_DIR}/foo # Access sub dir by CMAKE_CURRENT_SOURCE_DIR, same as ./foo foo # Access sub dir directly )
添加链接库路径,这里用到的变量可以参考 MCU 项目相关常量:
ameba_list_append(public_libraries ${c_SDK_LIB_APP_DIR}/lib_foo.a )
添加源文件,建议使用相对路径:
ameba_list_append(private_sources foo/foo.c bar/bar.c )
添加 private 头文件路径:
ameba_list_append(private_includes ../common # Access parent dir foo bar )
添加预定义:
ameba_list_append(private_definitions __RTOS__ MBEDTLS_CONFIG_FILE="mbedtls/config.h" )
添加编译选项:
ameba_list_append(private_compile_options -Wno-unused-parameter )
编译并链接一组新增的源文件
参考如下步骤:
拷贝模板
cmake/CMakeLists-template.cmake到源文件目录下,并改名为CMakeLists.txt参考 修改现有组件的编译配置 设置 public 部分 和 private 部分
private 部分 选择 ameba_add_internal_library 作为编译的 API
参考 CMakeLists.txt 调用关系图 在相应 MCU 项目下对应固件的
CMakeLists.txt中添加上述源码目录进行编译ameba_add_subdirectory(path/to/your/cmakelists)
编译,测试
适配具备独立编译系统的代码
对于具备独立编译系统(如采用 CMake/Makefile 的第三方组件),可通过非侵入式集成方案实现链接进入固件,具体实施可依据其编译系统类型选择适配策略。
拷贝模板
cmake/CMakeLists-template.cmake到一个合适的位置(比如为该代码组件新建一个 wrap 目录),并改名为CMakeLists.txt参考 修改现有组件的编译配置 设置 public 部分
在 private 部分 通过 ameba_add_subdirectory 添加原有
CMakeLists.txt编译,然后使用 ameba_port_standalone_internal_library 进行适配ameba_add_subdirectory(path/to/your/cmakelists) # Add the real CMakeLists.txt dir of the wrapped component ameba_port_standalone_internal_library(foo) # Add the real target of the wrapped component to link
参考 CMakeLists.txt 调用关系图 在相应固件的
CMakeLists.txt中添加上述CMakeLists.txt所在目录进行编译ameba_add_subdirectory(path/to/your/wrap/cmakelists)
编译,测试
拷贝模板
cmake/CMakeLists-template.cmake到一个合适的位置(比如为该代码组件新建一个 wrap 目录),并改名为CMakeLists.txt参考 修改现有组件的编译配置 设置 public 部分,特别是是设置
public_libraries为最终库文件的路径private 部分 使用如下代码,注意修改其中的路径或名称
add_custom_target(foo ALL # Replace foo with new name as you wish COMMAND ${CMAKE_COMMAND} -E echo "Building foo project" # Some prompt message while building COMMAND make -j -C /path/to/your/makefile all # Set your makefile path COMMENT "Building foo project using make" ) ameba_target_depend(${c_CURRENT_IMAGE} foo) # Add dependency, foo MUST be same as in add_custom_target above
参考 CMakeLists.txt 调用关系图 在相应固件的
CMakeLists.txt中添加上述CMakeLists.txt所在目录进行编译ameba_add_subdirectory(path/to/your/wrap/cmakelists)
编译,测试
链接预建的库文件
将预建的库文件 foo.a 链接到当前 image,参考 修改现有组件的编译配置 设置 public 部分。
具体步骤如下:
在组件 CMakeLists.txt 中将
foo.a的路径追加到public_libraries;其后的ameba_global_library()将会把这些库文件链接到当前 image;将相关的头文件目录追加到
public_includes;其后的ameba_global_include()将会把这些头文件目录加入全局的搜索路径。
注意
编译生成库文件 foo.a 的工具链和平台要与当前项目保持一致, 否则可能链接失败,或者执行时发生不可预测的错误。
常用的 CMake 接口和预设常量
list 操作
ameba_list_append
ameba_list_append(<list_name> [<value> ...])
向 list 中追加元素,支持追加多个,参数说明:
- list_name:
list 变量名
- value:
待追加的值
ameba_list_append_if
ameba_list_append_if(<condition> <list_name> [<value> ...] [p_ELSE <else value> ...])
基于一定的条件向 list 中追加元素,支持追加多个,参数说明:
- condition:
判定条件的变量名
- list_name:
list 变量名
- value:
当
condition成立时向 list 中追加的值- p_ELSE:
可选的关键字参数,其后面紧跟的值会在
condition不成立时追加到 list 中- else value:
当
condition不成立时向 list 中追加的值
注意
condition 所表示的变量未定义或定义了但布尔值为 FALSE 都会被认为不成立
ameba_list_append_ifnot
ameba_list_append_ifnot(<condition> <list_name> [<value> ...] [p_ELSE <else value> ...])
基于一定的条件向 list 中追加元素,支持追加多个,与 ameba_list_append_if() 功能相反,参数说明:
- condition:
判定条件的变量名
- list_name:
list 变量名
- value:
当
condition不成立时向 list 中追加的值- p_ELSE:
可选的关键字参数,其后面紧跟的值会在
condition成立时追加到 list 中- else value:
当
condition成立时向 list 中追加的值
注意
condition 所表示的变量未定义或定义了但布尔值为 FALSE 都会被认为不成立
ameba_list_append_ifdef
ameba_list_append_ifdef(<condition> <list_name> [<value> ...] [p_ELSE <else value> ...])
基于一定的条件向 list 中追加元素,支持追加多个,参数说明:
- condition:
判定条件的变量名
- list_name:
list 变量名
- value:
当
condition被定义时向 list 中追加的值( 注意:此时condition可以是FALSE)- p_ELSE:
可选的关键字参数,其后面紧跟的值会在
condition未定义时追加到 list 中- else value:
当
condition未定义时向 list 中追加的值
添加库
如下 API 用于将代码编译为静态库或者对已有 target 进行进一步处理
备注
这些 API 具有如下特点:
静态库被 哪个固件链接取决于是在哪个固件 的
CMakeLists.txt中添加,例如在 image2/CMakeLists.txt 中添加,则相应静态库会被固件image2链接如下 API 中的 target 实际名称是由
name参数及其他信息如 c_MCU_PROJECT_NAME,c_CURRENT_IMAGE 组合而成,用户可以在 API 调用过后使用变量c_CURRENT_TARGET_NAME来获取真实的 target 名称这些 API 内部会自动使用 全局编译配置 来编译当前 target
ameba_add_internal_library
ameba_add_internal_library(<name>
[p_SOURCES <sources> ...]
[p_INCLUDES <include dirs> ...]
[p_COMPILE_OPTIONS <compile options> ...]
[p_DEFINITIONS <definitions> ...]
[p_DEPENDENCIES <dependencies> ...]
)
添加一个静态库,执行编译且 相应 target 会被自动链接到当前固件,库文件输出到默认的 build 目录下,参数说明:
- name:
target 名称,实际完整的库文件为
lib_${name}.a- p_SOURCES:
target 源文件
- p_INCLUDES:
target 头文件路径
- p_COMPILE_OPTIONS:
target 编译选项
- p_DEFINITIONS:
target 预定义
- p_DEPENDENCIES:
target 依赖
注意
该 API 生成的库文件在 build 目录下,其内部不会检查 CONFIG_AMEBA_RLS,始终生效
ameba_port_standalone_internal_library
ameba_port_standalone_internal_library(<name>)
将一个 已有 静态库 target 添加到当前固件的链接 :
- name:
target 名称
小技巧
特别适用于非侵入性的适配第三方库的 CMakeLists.txt,参考 适配具备独立编译系统的代码
注意
该 API 其内部不会检查 CONFIG_AMEBA_RLS,始终生效
添加子目录
ameba_add_subdirectory
ameba_add_subdirectory(<dir>)
添加一个目录用于编译,可参考 add_subdirectory,此外,当 dir 为外部路径时会取其最后一级目录名作 binary_dir,参数说明:
- dir:
要添加编译的目录
ameba_add_subdirectory_if
ameba_add_subdirectory_if(<condition> <dir>)
基于一定的条件添加一个目录用于编译,其他同 ameba_add_subdirectory,参数说明:
- condition:
判定条件的变量名
- dir:
条件成立时要添加编译的目录
ameba_add_subdirectory_ifnot
ameba_add_subdirectory_ifnot(<condition> <dir>)
基于一定的条件添加一个目录用于编译,其他同 ameba_add_subdirectory,参数说明:
- condition:
判定条件的变量名
- dir:
条件不成立时要添加编译的目录
注意
condition 所表示的变量未定义或定义了但布尔值为 FALSE 都会被认为不成立
ameba_add_subdirectory_if_exist
ameba_add_subdirectory_if_exist(<dir>)
当路径不存在,或路径下没有 CMakeLists.txt 时,直接静默返回,路径下存在 CMakeLists.txt 时同 ameba_add_subdirectory,参数说明:
- dir:
如果存在时要添加编译的目录
常量定义
CMake 脚本 cmake/global_define.cmake 中定义了一些常量可以直接用于编写脚本,列举部分如下:
常量类型 |
变量名 |
值 |
|---|---|---|
通用常量 |
|
仓库根目录 |
|
|
|
|
|
|
SoC 项目相关常量 |
|
["amebadplus", "amebalite", "amebasmart"] 之一 |
|
|
|
MCU 项目相关常量 |
|
["km0", "km4", "kr4", "ca32", ...] 之一 |
|
如果 MCU 项目文件夹名称为 |
|
|
当前固件 target 名称,如 |
|
|
当前 MCU 项目 Kconfig 文件路径 |
|
|
|
|
|
|
|
|
|
|
组件相关常量 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
进阶阅读
cmake 目录结构如下:
cmake
├── flags # Global compile and link options
│ ├── ca32
│ │ ├── compile_options.cmake
│ │ └── link_options.cmake
│ ├── common
│ │ ├── compile_options.cmake
│ │ └── link_options.cmake
│ ├── km0
│ │ ├── compile_options.cmake
│ │ └── link_options.cmake
│ ├── km4
│ │ ├── compile_options.cmake
│ │ └── link_options.cmake
│ └── kr4
│ ├── compile_options.cmake
│ └── link_options.cmake
├── CMakeLists-template.cmake # CMakeLists.txt template for component
├── common.cmake # Project related APIs
├── global_define.cmake # Global defined parameters
├── utility_base.cmake # Utility APIs (lower level)
├── utility.cmake # Utility APIs (upper level)
└── toolchain # Toolchain defines
├── ameba-toolchain-asdk-10.3.1.cmake
├── ameba-toolchain-asdk-12.3.1.cmake
├── ameba-toolchain-check.cmake
├── ameba-toolchain-common.cmake
└── ameba-toolchain-vsdk-10.3.1.cmake
全局编译配置
全局编译配置指在一定范围内,所有的组件共享的编译配置内容。
编译配置主要内容
源文件 (sources)
头文件路径 (include directories)
编译选项 (compile options)
预定义 (definitions)
链接选项 (link options)
链接库 (link libraries)
备注
源文件、头文件路径、编译选项、预定义 用于组件编译。
链接选项 和 链接库 用于固件链接。
全局编译配置来源
主要有两部分来源:
cmake/flags中的设置各个组件 public 部分 中追加的内容
注意
最终全局编译配置中各组件追加部分的顺序(如头文件路径的顺序),取决于组件被固件 add 的顺序。
这个顺序是不可靠的,在顺序敏感的配置中(如头文件路径),最好通过别的方式处理,详见 private 部分。
全局编译配置作用域
全局编译配置的作用域为各 MCU 项目,如 RTL8721Dx 中 km0 和 km4 各有一套全局编译配置, RTL8730E 中 km0,km4 及 ca32 各有一套全局编译配置,它们之间相互隔离。
同一 MCU 项目下的组件编译时使用相同的全局编译配置,参考如下示意图:
其中:
可以看到每个 MCU 项目都各有一个独立的编译配置作用域。
同一个 MCU 项目下的不同固件
image1,image2,image3使用相同的编译配置。同一个组件在不同的 MCU 项目中会使用 不同的全局编译配置 分别编译,如上图中的
at_cmd在project_mcu1和project_mcu2的image2中分别被编译。
查看某个源文件的详细编译参数
在相应源文件开头加入如下代码或者加入一些语法错误然后进行编译,编译器会在相应源文件处出错并打印详细编译参数
#error debug
一些可用于复杂逻辑处理的判断类型
SOC 类型
SOC 类型如 amebadplus,amebalite,amebasmart 可以通过如下变量判断: CONFIG_AMEBADPLUS,CONFIG_AMEBALITE,CONFIG_AMEBASMART :
if(CONFIG_AMEBADPLUS)
# Add code for amebadplus here
elseif(CONFIG_AMEBALITE)
# Add code for amebalite here
elseif(CONFIG_AMEBASMART)
# Add code for amebasmart here
endif()
注意
SOC 类型的区分应当尽可能消除,更好的方法是使用特性之类的开关配置替代
如果一定要使用应秉持向上兼容的原则,即未来有新增 SOC 类型时 无需改动此处逻辑 (比如增加新的
elseif),例如:file:component/rtk_coex/CMakeLists.txt中的写法:if(NOT CONFIG_AMEBAD) if (CONFIG_COEXIST_HOST) include(rtk_coex_api.cmake) endif() endif()
MCU 类型
SOC 类型如 km0,km4,kr4,ca32 等,可以通过 STR 类型变量 c_MCU_TYPE 获取:
if(${c_MCU_TYPE} STREUQAL "km0")
# Add code for km0 here
elseif(${c_SOC_TYPE} STREUQAL "km4")
# Add code for km4 here
elseif(${c_SOC_TYPE} STREUQAL "ca32")
# Add code for ca32 here
endif()
CMake 常见的一些 debug 方法
日志
可以使用 CMake 内置 message() 或可读性更好的 ameba_debug(),ameba_info(),ameba_warning(),ameba_fatal()
使用如下方式打印日志, CMake 在配置阶段执行到代码处会停止,通常可以用于分析代码执行情况
message(FATAL_ERROR "stop here")
常见 CMake 错误排查
小技巧
排查 CMake 错误要从终端输出中的 第一个 错误开始看起
Undefined Reference 错误
该错误一般发生在链接生成 axf 文件阶段,常见原因有如下几类:
原因一:符号所在库文件未被链接
原因二:符号所在源文件未被编译
原因三:符号所在代码块未被编译
下面以 RTL8721Dx 为例,针对上述原因依次进行排查,如下是一个链接错误的输出(这里仅保留了部分信息用于展示):
[5/42] Linking C executable project_km4/asdk/make/image2/target_img2_km4.axf
FAILED: project_km4/asdk/make/image2/target_img2_km4.axf
ccache /opt/rtk-toolchain/asdk-10.3.1-4354/linux/newlib/bin/arm-none-eabi-gcc
-O2
-o project_km4/asdk/make/image2/target_img2_km4.axf
-Wl,--whole-archive
project_km4/asdk/make/image2/at_cmd/lib_at_cmd.a
project_km4/asdk/make/image2/swlib/lib_swlib.a
project_km4/asdk/make/image2/file_system/fatfs/lib_fatfs.a
project_km4/asdk/make/image2/file_system/littlefs/lib_littlefs.a
project_km4/asdk/make/image2/file_system/kv/lib_kv.a
project_km4/asdk/make/image2/file_system/vfs/lib_vfs.a
project_km4/asdk/make/image2/fwlib/lib_fwlib.a
project_km4/asdk/make/image2/hal/lib_hal.a
project_km4/asdk/make/image2/misc/lib_misc.a
project_km4/asdk/make/image2/lwip/lib_lwip.a
-Wl,--no-whole-archive
-lm
-O2
-o project_km4/asdk/make/image2/target_img2_km4.axf
-Wl,--whole-archive
project_km4/asdk/make/image2/at_cmd/lib_at_cmd.a
project_km4/asdk/make/image2/cmsis-dsp/lib_cmsis_dsp.a
-Wl,--no-whole-archive
-lm
-lstdc++
ld: amebadplus_gcc_project/project_km4/asdk/lib/soc/lib_chipinfo.a(ameba_rom_patch.o): in function `io_assert_failed':
(.text.io_assert_failed+0x12): undefined reference to `rtk_log_write_nano'
针对上述错误首先确认基本信息,通过高亮行 1,2,33,34 可知:
出现链接问题的 MCU 项目 是:
km4出现链接问题的 固件 是:image2,文件名 是:
target_img2_km4.axf未定义的符号是:
rtk_log_write_nano,其是在函数io_assert_failed()中被调用的进一步确认:
包含该符号定义的 源文件:
log.c包含该源文件的 组件:
swlib该组件的生成的 库文件:
lib_swlib.a
然后依次排查上述原因:
符号所在库文件未被链接
排查述错误信息中的链接参数是否包含 库文件:
lib_swlib.a通过上述错误信息第 9 行可知
project_km4/asdk/make/image2/swlib/lib_swlib.a已经被 image2 加入了链接,如果没有则需要排查:image2 的
CMakeLists.txt中是否 addswlib组件
符号所在源文件未被编译
在 CMake 的
build目录中的 MCU 项目目录project_km4查找文件是否存在log.o:
执行命令:
find build/project_km4 -name log.o
结果如下:
build/project_km4/asdk/make/image1/swlib/CMakeFiles/swlib_target_loader_km4.dir/log.o
build/project_km4/asdk/make/image2/swlib/CMakeFiles/swlib_target_img2_km4.dir/log.o
执行命令:
dir /s build\project_km4\log.o
结果如下:
C:\Users\sdk\amebadplus_gcc_project\build\project_km4\asdk\make\image1\swlib\CMakeFiles\swlib_target_loader_km4.dir 的目录
2025/03/12 14:56 21,096 log.o
1 个文件 21,096 字节
C:\Users\sdk\amebadplus_gcc_project\build\project_km4\asdk\make\image2\swlib\CMakeFiles\swlib_image2_km4.dir 的目录
2025/03/12 14:56 21,096 log.o
1 个文件 21,096 字节
查看黄色标记部分,如果未出现表示
log.c未被编译,此时需要排查swlib的CMakeLists.txt中的问题如: 遗漏源文件,逻辑错误等
符号所在代码块未被编译
参考 原因二 中步骤找到
log.o路径使用如下命令(这里需要用到工具链, 具体路径请参考 安装工具链)查看
log.o中是否包含符号rtk_log_write_nano:执行命令:
/opt/rtk-toolchain/asdk-10.3.1/linux/newlib/bin/arm-none-eabi-nm build/project_km4/asdk/make/image2/swlib/CMakeFiles/swlib_target_img2_km4.dir/log.o | grep -w rtk_log_write_nano #Or use default nm: nm build/project_km4/asdk/make/image2/swlib/CMakeFiles/swlib_target_img2_km4.dir/log.o | grep -w rtk_log_write_nano
执行命令:
C:\rtk-toolchain\asdk-10.3.1-4354\mingw32\newlib\bin\arm-none-eabi-nm.exe build\project_km4\asdk\make\image2\swlib\CMakeFiles\swlib_image2_km4.dir\log.o | findstr /R "\<rtk_log_write_nano\>"
如果输出 不包含 如下内容(注意其中的
T)则表明log.a未包含符号,此时需要排查log.c中的条件编译是否正确00000000 T rtk_log_write_nano
执行外部命令参数错误
通常是在调用 CMake 接口 add_custom_target() 或 add_custom_command() 中 COMMAND 中的命令参数有误导致,而根源更多是因为某些 CMake 变量为空导致。典型错误输出如下, 特征是会包括 CMake 的 -E 参数的 usage:
FAILED: build/lib_atcmd.a
/usr/bin/cmake -E copy build/lib_atcmd.a
CMake Error: cmake version 3.30.2
Usage: /usr/bin/cmake -E <command> [arguments...]
Available commands:
capabilities - Report capabilities built into cmake in JSON format
cat [--] <files>... - concat the files and print them to the standard output
chdir dir cmd [args...] - run command in a given directory
...
出现此错误对应的 CMake 代码如下:
add_custom_command(
OUTPUT lib/lib_atcmd.a
COMMAND ${CMAKE_COMMAND} -E copy build/lib_atcmd.a ${output_path}
DEPENDS build/lib_atcmd.a
)
在上述代码中,如果变量 output_path 为空,拷贝命令就会变成了上述错误信息第 2 行缺少目的路径从而导致报错
拷贝替换了一个源文件,但没有重新编译
CMake 编译系统默认是增量编译,即源码或头文件发生变化时才会重新编译,而其检测变化的依据是 文件的修改时间是否比上次更新
当从别处拷贝了一个修改时间更老的文件,如果文件修改时间戳保持不变(Windows 下资源管理器默认行为)就不会触发增量编译
解决方法:
方法一:Windows 环境中新建文件,然后拷贝内容
方法二:执行
build.py -c进行清理
阻止编译警告错误
全局编译配置中 cmake/flags/common/compile_options.cmake 默认使用了 -Werror 选项,即所有编译警告都会当作编译错误处理,如需解除该设定可参考下列方法:
方法一:仅对当前组件生效,在当前组件的
CMakeLists.txt添加编译选项:ameba_list_append(private_compile_options -Wno-error )
方法二:对所有组件生效,修改全局编译配置
cmake/flags/common/compile_options.cmake注释掉-Werror即可
使用 no-whole-archive 的方式链接某个静态库
Ameba CMake API 处理的静态库默认都是以 whole-archive 的方式被固件链接的。如果需要强制静态库使用 no-whole-archive, 针对不同的情况方案如下:
情况一:使用 ameba_add_internal_library 编译的静态库
在
ameba_add_internal_library()函数中增加参数p_NO_WHOLE_ARCHIVE:ameba_add_internal_library(at_cmd p_NO_WHOLE_ARCHIVE p_SOURCES ${private_sources} p_INCLUDES ${private_includes} p_DEFINITIONS ${private_definitions} p_COMPILE_OPTIONS ${private_compile_options} p_DEPENDENCIES ${c_BUILD_INFO} )
情况二: 使用
public_libraries加入链接的静态库在调用
ameba_global_library()时加入参数p_NO_WHOLE_ARCHIVEameba_global_library(${public_libraries} p_NO_WHOLE_ARCHIVE)