- 2025-03-28
-
回复了主题帖:
邀你聊一聊:学习MCU的正确方法和你的学习之路
学习MCU的: 最终竞争力不在于会多少型号,而是能否用MCU解决真实的工程问题。
- 2025-03-25
-
回复了主题帖:
超迷你却超强大!明远智睿 SSD2351 开发板深度剖析
图片呢?来张帅图看看,渲染图也可也啊,图文并茂更有吸引力
- 2025-03-22
-
回复了主题帖:
用了一年的休息时间给我女儿设计的电动车
哇大哥这厉害啊,这你女儿的同伴们这不羡慕的很
- 2025-03-21
-
发表了主题帖:
《CMake构建实战》- 总结
本帖最后由 eew_Eu6WaC 于 2025-3-21 15:26 编辑
《CMake构建实战》- 总结
《CMake构建实战》第一章-构件之旅
《CMake构建实战》第二章-CMake简介和安装
《CMake构建实战》第三章-基础语法
《CMake构建实战》第四章-常用命令
《CMake构建实战》第五章-CMake快速排序
《CMake构建实战》第六章-CMake构建初探
《CMake构建实战》第七章-构建目标和属性(上)
《CMake构建实战》第七章-构建目标和属性(下)
《CMake构建实战》第八章-生成器表达式
《CMake构建实战》第九章-模块
《CMake构建实战》第十章-策略和向后兼容
《CMake构建实战》- CMake在HarmomyOS中的实际使用
《CMake构建实战》是一本全面介绍CMake构建系统的书籍,详细地讲解了CMake的基础知识、核心概念和高级用法。以下是各章节的简要总结:
第一章:构件之旅
从简单的代码编译和构建入门,介绍了单源文件程序、多源程序、静态库和动态库的构建过程
第二章:CMake简介和安装
介绍了CMake的基本概念和优势,包括其高效性、强大性和广泛的用户群体。同时,在Linux平台上安装了CMake
第三章:基础语法
讲解了CMake的基础语法,包括命令调用、命令参数、变量、列表、条件控制、循环和命令定义等内容。
第四章:常用命令
介绍了CMake中的一些常用命令,如文件操作命令、数学运算命令、字符串操作命令等。
第五章:CMake快速排序
通过一个快速排序的案例,展示了如何使用CMake实现复杂的算法,并结合了之前章节所学的知识点
第六章:CMake构建初探
介绍了CMake项目的生命周期,包括配置阶段、生成阶段、构建阶段、安装阶段和打包阶段。并且介绍了如何使用CMake命令行进行配置和生成操作
第七章:构建目标和属性(上)
介绍了CMake中的构建目标和属性,包括二进制构建目标(如可执行文件、静态库、动态库等)和伪构建目标(如接口库、导入目标、别名目标等)。
第七章:构建目标和属性(下)
介绍了CMake中的属性相关的命令,如设置目标链接库、设置宏定义、设置编译参数等。
第八章:生成器表达式
介绍了CMake中的生成器表达式,包括布尔型生成器表达式和字符串生成器表达式,以及如何在CMake中使用这些表达式来获取与构建系统相关的信息
第九章:模块
介绍了CMake中的模块,包括功能模块和查找模块。功能模块用于代码复用,查找模块用于搜索第三方依赖软件包。
第十章:策略和向后兼容
介绍了CMake中的策略和向后兼容性,包括CMake策略、变量、命令和属性的向后兼容性处理方法。
CMake在HarmonyOS中的实际使用
本章通过一个实际案例,展示了如何在HarmonyOS中使用CMake进行项目构建,包括环境配置、CMakeLists.txt文件编写和构建过程等内容。
通过阅读这本书,我大致了解了CMake构建系统,并能够简单灵活的使用模板应用于实际项目中。
- 2025-03-06
-
回复了主题帖:
手把手教你用聆思CSK6大模型开发板接入火山引擎 满血版 DeepSeek-R1
乍一看以为是本地接入满血的deepseek,这应该算使用API吧,这么多tokens大概能用多久?
- 2025-03-05
-
回复了主题帖:
【航芯ACM32F403开发板测评】+RTC与电子时钟
好简洁的代码,看着使用非常方便,内置RTC精确度高嘛?
- 2025-02-28
-
回复了主题帖:
[树莓派Pico 2 RP2350开发板] pwm的学习
树莓派RP2350和老版本的树莓派PR2040有什么大的区别呢?
-
回复了主题帖:
【树莓派Pico 2 RP2350开发板】 测评 【一】 开箱 + 编译uf2 固件 + blinkLed
详细的教程!什么时候树莓派官方这种板子的接口可以换成Tpye-C
- 2025-02-27
-
回复了主题帖:
自制 AirTag,支持安卓/鸿蒙/PC/Home Assistant,无需拥有 iPhone
设备附件使用的苹果设备多,应该信号就好吧,到荒无人烟的地方估计就找不着了
-
回复了主题帖:
《CMake构建实战》第十章-策略和向后兼容
Jacktang 发表于 2025-2-27 07:29
渐进式重构 CMake 程序,在用的时候会提示警告,让使用者及时偿还技术债务来更好过度到新版本。
怎么理 ...
"及时偿还技术债务"这个概念源自于软件开发领域,是当项目随着时间推移积累了技术上的“债务”。这些“债务”可以是代码质量不高、设计缺陷、未完成的特性或者使用了过时的技术等。如果不加以处理,这些问题可能会导致后续开发效率降低、维护成本增加、出现更多bug等
- 2025-02-26
-
回复了主题帖:
《CMake构建实战》- CMake在HarmomyOS中的实际使用
蛋黄派派主 发表于 2025-2-25 21:50
感谢分享
-
回复了主题帖:
《CMake构建实战》- CMake在HarmomyOS中的实际使用
craigtao 发表于 2025-2-25 10:11
不错,CMake现在是主流,不管是纯应用开发还是嵌入式系统开发,cmake都占有一席之地,期待楼主可以多分享一 ...
是的对于嵌入式开发,一般厂家或者IDE那边都会提供对应的模板,对于开发使用有很大的便利
-
回复了主题帖:
【NUCLEO-WB09KE测评】五、蓝牙数据收发实现
板子可以接收到APP发送的数据,但为啥发送会失败呢?
Fail : aci_gatt_srv_notify TESTRESPONSE command, error code: 0x C
- 2025-02-25
-
发表了主题帖:
《CMake构建实战》- CMake在HarmomyOS中的实际使用
本帖最后由 eew_Eu6WaC 于 2025-2-25 09:17 编辑
《CMake构建实战》- CMake在HarmomyOS中的实际使用
本章只注重CMake在HarmonyOS中NDK的应用,NDK其实就是 Napi 的跨语言的调用,就先看看里面是如何使用CMake构建这一部分,有兴趣可以深入讲讲鸿蒙里面跨语言的用法和机制
下面是DevEco Studio中创建的Native C++中CMake相关的代码
可以看到在最顶层有一个CMakeLists.txt文件,这是顶层文件,下面分析一下代码
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(NDKDemo)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED napi_init.cpp)
add_subdirectory(demo)
target_link_libraries(entry PUBLIC libace_napi.z.so calc)
cmake_minimum_required 设置了最低版本的要求为3.5.0
project 定义了项目名称为NDKDemo
设置了一个变量:NATIVERENDER_ROOT_PATH,为当前源目录的路径
条件判断,如果定义了PACKAGE_FIND_FILE,就包含这个路径
include_directories 添加到编译器的头文件所搜索路径中去
add_subdirectory 创建了一个共享库entry,是由源码napi_init.cpp编译而成
target_link_libraries 将库libace_napi.z.so和calc链接到目标entry
demo这个文件是自己创建的,在其中也添加了CMakeLists.txt,编译器也会找到这里进行处理,原理非常简单,要将添加的4个文件也一起打包成库以便使用
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})
include_directories 将当前目录和当前目录下的include包含进去,这样就将head.h添加到了编译器搜索路径中,在head.h中申明了src中所使用到的所有函数
file 可以生成一个源文件列表,在当前目录下的scr目录中下
link_directories 链接可执行文件或库时,链接器会在这个目录下查找所需的库文件
add_library 创建了一个名为calc的共享库,也就是在顶层文件中链接的
编译过后,就可以得到需要的共享库了
-
回复了主题帖:
这个新功能蛮不错
哈哈哈哈哈哈这个英文界面切换功能强的,专业英语可不好翻译的
- 2025-02-24
-
发表了主题帖:
《CMake构建实战》第十章-策略和向后兼容
《CMake构建实战》第十章-策略和向后兼容
CMake非常重视向后兼容性,其中策略机制是解决该问题的关键。它既能确保旧代码在新版本 CMake 中配置生成,又能针对弃用特性给出警告,还会适时清理旧特性,推动自身发展。所以本文会介绍一下相关概念、命令及程序重构方法。
CMake策略(以 CMP0115 为例)
CMake 策略名称以 CMP 加四位整数命名,如 CMP0115。每个策略对应新旧两种行为,具体可以查阅官方文档进行了解详情。在官网中会依次列出CMake从新到旧的各个版本进入的策略。比如CMP0115的详情页中可以看到,在CMake 3.19及以前的版本中允许在add_executable等命令中省略源文件扩展名,而新版本则要求必须显式指定。
那么CMake文件是如何发现版本升级了呢?那就要和cmake_minimum_required命令有关了,见下小节。
指定CMake最低版本要求:cmake_minimum_required
这个命令是用于指定运行当前 CMake 程序所需的最低版本,若当前版本低于要求则报错!
这个命令不仅适用于目录程序,在CMake脚本程序、模块程序中都可以调用该命令来设置CMake的最低版本要求。
使用非常的简单:cmake_minimum_required(VERSION <最低版本>)
可以看到上面的程序和运行结果,当我把最小版本的设置改成大于当前安装的版本时,运行就会报错终止。
管理策略行为:cmake_policy
cmake_policy命令中有多个子命令,可以分别用于获取和显示指定策略的行为:
按策略名称设置策略行为:cmake_policy(SET <策略名称> NEW|OLD) - 可手动设置指定策略的行为为 NEW 或 OLD,方便在代码重构后或不想看到警告时进行调整。
获取策略行为:cmake_policy(GET <策略名称> <结果变量>) - 用于获取指定策略的行为并存储到结果变量中,若策略未设置,变量为空值,否则为 NEW 或 OLD。
按 CMake 版本设置策略行为:cmake_policy(VERSION <最低版本>[...<策略兼容的最高版本>]) - 依据指定的版本范围设置相关策略行为,cmake_minimum_required命令也支持类似设置。
管理 CMake 策略栈:子目录和模块程序会创建新的策略作用域并加入策略栈,cmake_policy操作的是栈顶策略。cmake_policy(PUSH) 和 cmake_policy(POP) 分别用于策略行为的入栈和出栈,便于程序重构升级时管理策略。sudo apt-get install openssh-client
渐进式重构 CMake 程序
策略可以很好的让CMake兼容古老的代码,同时在使用的时候会提示警告,让使用者及时偿还技术债务来更好过度到新版本。
局部代码重构并启用新行为:对部分代码重构后,借助策略栈实现细粒度的策略行为设置与恢复,若某个子目录重构完成,可直接对该目录采用指定策略的 NEW 行为。
禁用警告信息:将策略行为设为 OLD 可禁用警告,局部设置可通过策略栈完成,适用于暂时不想重构但不想受警告干扰的情况。
同时兼容旧版 CMake:利用if(POLICY)判断策略是否存在,根据 CMake 版本进行不同的项目配置,以兼容新旧版本,重构后可只保留新行为。
为全部策略采用新行为:重构完成后,调整cmake_minimum_required命令的<策略兼容的最高版本>参数,使程序在不同版本下按相应设置运行,同时清理冗余代码。
完全切换到新版 CMake:修改cmake_minimum_required命令的<最低版本>参数,强制用户使用新版 CMake,删除if(POLICY)条件分支,优化代码
好的,CMake相关的基础知识到这里结束,下面会去看下鸿蒙在添加编译NAPI应用的时候如何使用CMake的
-
回复了主题帖:
《CMake构建实战》第八章-生成器表达式
Jacktang 发表于 2025-2-22 15:04
虽然是两类表达式,布尔型生成器表达式 和 字符串生成器表达式,细分也不少
是的呢要掌握的东西确实不少,先大致了解用的时候查漏补缺就快啦
- 2025-02-22
-
回复了主题帖:
【Raspberry Pi 5测评】树莓派5学习笔记08(部署YOLOv5进行目标检测)
树莓派还是挺好用的,YOLOv5模型的识别的准确度咋样呢?
-
回复了主题帖:
【正点原子i.MX93开发板】测评 + 二、环境搭建 + 开发板连接上位机
正点原子的这块板子对应的教程和资料丰富吗?
-
发表了主题帖:
《CMake构建实战》第九章-模块
《CMake构建实战》第九章-模块
这一章围绕CMake模块展开,介绍了其功能模块、查找模块的使用方法、自定义查找模块的编写步骤。并且有着大量案例,对每个用法展示运行效果。
引用功能模块
CMake 模块程序是代码复用单元,像类库一样被引用。其预置模块丰富,位于安装目录的share/…/Modules子目录,可通过include命令引用。
常用的预置功能模块
1. 调试模块:CMakePrintHelpers 的 cmake_print_properties 和 cmake_print_variables 命令分别用于输出属性和变量值。
cmake_minimum_required(VERSION 3.20)
project(print-system-info)
include(CMakePrintSystemInformation)
cmake_minimum_required(VERSION 3.20)
project(print-helpers VERSION 1.0)
include(CMakePrintHelpers)
cmake_print_properties(DIRECTORIES .
PROPERTIES
BINARY_DIR
SOURCE_DIR
)
2. 环境检查模块:有多个检查功能的模块,CheckSourceCompiles用于检查源程序能否编译、CheckSymbolExists用于检查符号是否存在,CheckStructHasMember
用于检查结构体/类是否存在指定的成员变量
cmake_minimum_required(VERSION 3.20)
project(check-source-compiles)
include(CheckSourceCompiles)
check_source_compiles(C "int main() { return 0; }" res)
message("${res}") # 输出:1
set(CMAKE_REQUIRED_QUIET True)
check_source_compiles(C "invalid code" res2)
message("${res2}") # 输出空值
cmake_minimum_required(VERSION 3.20)
project(check-symbol-exists)
include(CheckSymbolExists)
check_symbol_exists(printf "stdio.h" res)
message("${res}") # 输出:1
cmake_minimum_required(VERSION 3.20)
project(check-struct-member)
include(CheckStructHasMember)
check_struct_has_member("std::pair<int,int>" "first" "utility" res
LANGUAGE CXX)
3. 生成导出头文件模块:GenerateExportHeader模块用于生成导出头文件,定义多种宏,用于解决库开发中接口头文件编写的复杂问题
痛点:
各个编译器对导出接口所需的属性不同,隐藏符号的编译选项也不同;另外,有时需要更灵活地配置接⼝,使其能够同时⽤于静态库和动态库;⼜或者需要通过⼀些宏来标记某些过时的接⼝为弃⽤状态…… 通常会编写⼀个头⽂件,⽤宏来判断不同的编译器,再定义⼀些宏来对应不同的属性设置等,以此将不同编译器的差异隐藏起来。每次开发⼀个库时,恐怕都会这样“重复造轮⼦”。
解决:
CMake提供了GenerateExportHeader模块。
//CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(export-api)
include(GenerateExportHeader)
add_library(print SHARED print.c)
generate_export_header(print
# DEFINE_NO_DEPRECATED # 取消注释以定义忽略弃用接口宏
) # 导出头文件会被生成到二进制目录中
# 将当前二进制目录加到头文件搜索目录中
target_include_directories(print PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
# 定义print_EXPORTS宏,表明正在构建该库(若不定义,则表示使用该库)
target_compile_definitions(print PRIVATE print_EXPORTS)
# 设置目标属性,默认隐藏符号和内联函数
set_target_properties(print PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN 1
)
# 也可以通过下面两个变量,设置上述两个目标属性的默认值
# set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
# 主程序
add_executable(main main.c)
target_link_libraries(main PRIVATE print)
if(MSVC)
# 为MSVC编译器启用级别3的警告
target_compile_options(main PRIVATE /W3)
endif()
# 静态库
add_library(print_static STATIC print.c)
target_include_directories(print_static PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_compile_definitions(print_static PUBLIC PRINT_STATIC_DEFINE)
//main.c
#include "print.h"
int main() {
print();
old_print();
return 0;
}
//print.c
#include "print.h"
#include <stdio.h>
void print() { printf("Hello\n"); }
void _internal() {}
#ifndef PRINT_NO_DEPRECATED
void old_print() { printf("Hi\n"); }
#endif // PRINT_NO_DEPRECATED
//print.h
#ifndef PRINT_H
#define PRINT_H
#include "print_export.h"
PRINT_EXPORT void print();
PRINT_NO_EXPORT void _internal();
#ifndef PRINT_NO_DEPRECATED
PRINT_DEPRECATED_EXPORT void old_print();
#endif // PRINT_NO_DEPRECATED
#endif // PRINT_H
查找模块
查找模块(find module)是⼀系列用于搜索第三⽅依赖软件包(包括库或可执⾏⽂件)的模块。对查找模块的引用⼀般不使用include命令,⽽是使用find_package命令。
查找软件包命令(模块模式)
find_package命令的模块模式用于搜索第三方依赖软件包,调用 “Find < 软件包名>.cmake” 模块,可设置多种参数,执行后会定义相关变量。
格式:
find_package(<软件包名> [<版本号>] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [<⼦组件>...]]
[OPTIONAL_COMPONENTS <可选⼦组件>...]
[NO_POLICY_SCOPE])
用点:在开发应用的时候,经过会碰到 undefined reference to `xxxxx'。以`pthread_create'为例,就是:undefined reference to `pthread_create。可以在编译的时候添加一个编译选项来链接这个库。
难点:如何在CMake目录程序中设置链接呢?
解决:1. 可以通过判断当前的操作系统,来链接上对应的编译选项。2、可以使用CMake查找线程的模块:FindThreads。(可以针对不同的平台找到对应的线程酷)
//CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(find-threads)
# 调用FindThreads模块,它会创建一个导入目标Threads::Threads
find_package(Threads)
add_executable(main main.cpp)
# 如果不链接Threads::Threads,在Linux环境中构建会出错:
# undefined reference to `pthread_create'
target_link_libraries(main PRIVATE Threads::Threads)
#include <cstdio>
#include <thread>
void worker(int i) { printf("worker%d\n", i); }
int main() {
std::thread th(worker, 0);
th.join();
return 0;
}