这篇文章将介绍CmakeLists.txt的编写规则。
- [cmake.org/cmake/help/…]
- 这个文档中涉及到的代码,需要从[github.com/Kitware/CMa…] 仓库中下载源码,源码位置:Help/guide/tutorial
HarmonyOS SDK自带的官方文档
- Library/Huawei/Sdk/openharmony/9/native/build-tools/cmake/doc/cmake/html/guide/tutorial/index.html
实验代码
实验设备
MacBook Pro
编译命令
- cmake -S .
- cmake --build .
- ./xxx
以 A-hello-cmake 为例
- cd A-hello-cmake
- cmake -S .
- cmake --build .
- ./hello_cmake
命令行终端中输出:Hello CMake! 字样
以下为前三个阶段目录结构的变化说明
没执行任何命令前的目录结构
执行 cmake -S .
之后的结构
执行 cmake --build .
之后的结构
执行 ./hello_cmake
你会发现,所有编译产出都和源文件在同一级,接下来我们将所有输出指定到.cxx文件夹下边
使用上边的 A-hello-cmake 你可以尝试一下
cmake -S . -B .cxx
cmake --build .cxx
执行 cmake -S . -B .cxx
之后的结构
执行 cmake --build .cxx
之后的结构
以上编译过程,第一没有指定cmake, 第二没有指定cpu架构
cmake可以在电脑中存在多个,比如Android开发环境和HarmonyOS开发环境下都配置有CMake
由于我们要运行的库最终是在手机上,所以需要设置交叉式编译CPU架构,如 arm64-v8a
, armeabi-v7a
CMakeLists.txt 示例
HarmonyOS 源码移植编译中,最核心的是CMakeLists.txt文件的配置,这里将展示常用的语句编写规则。
1. 生成一个可执行文件
# $ cmake --version
# 设置本脚本支持的最小cmake版本,通过EevEco Studio 创建c++时,默认会自动生成好
cmake_minimum_required(VERSION 3.5)
# c++/c 工程的名称
# 这里也可以设置工程版本号,如 project(hello_cmake VERSION 1.0)
project (hello_cmake)
# 生成可执行的文件, hello_cmake是最终可运行文件的名称, main.cpp为其对应的源文件
# 注意,手机中编译时,使用的是add_library,因为手机应用需要的库
add_executable(hello_cmake main.cpp)
2. 为可执行文件,添加头文件
# $ cmake --version
# 设置本脚本支持的最小cmake版本,通过EevEco Studio 创建c++时,默认会自动生成好
cmake_minimum_required(VERSION 3.5)
# c++/c 工程的名称
# 这里也可以设置工程版本号,如 project(hello_cmake VERSION 1.0)
project (hello_headers)
# set 是用来创建变量,其值为变量后边带的文件
# 这里是创建一个SOURCES变量,其代表了Hello.cpp 和 main.cpp两个文件
set(SOURCES
src/Hello.cpp
src/main.cpp
)
# 生成一个名叫 hello_headers 的可执行的文件,其内容由 SOURCES 变量所对应的文件组成
add_executable(hello_headers ${SOURCES})
# 指定 hello_headers 所依赖的头文件,头文件即可以使用相对路径,也可以使用绝对路径
# PROJECT_SOURCE_DIR 是CMake中的内置变量,代表当前CMakeList.txt对应的工程根目录
target_include_directories(hello_headers
PRIVATE
${PROJECT_SOURCE_DIR}/include
)
3. 生成一个动态库
# $ cmake --version
# 设置本脚本支持的最小cmake版本,通过EevEco Studio 创建c++时,默认会自动生成好
cmake_minimum_required(VERSION 3.5)
# c++/c 工程的名称
# 这里也可以设置工程版本号,如 project(hello_cmake VERSION 1.0)
project (hello_cmake)
# 生成名为 hello_cmake 的动态库,main.cpp为其对应的源文件
add_library(hello_cmake SHARED main.cpp)
# 指定 hello_headers 所依赖的头文件,头文件即可以使用相对路径,也可以使用绝对路径
# PROJECT_SOURCE_DIR 是CMake中的内置变量,代表当前CMakeList.txt对应的工程根目录
target_include_directories(hello_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
4. 动态库依赖SDK动态库
# $ cmake --version
# 设置本脚本支持的最小cmake版本,通过EevEco Studio 创建c++时,默认会自动生成好
cmake_minimum_required(VERSION 3.4.1)
# c++/c 工程的名称
# 这里也可以设置工程版本号,如 project(hello_cmake VERSION 1.0)
project(HarveyCmake VERSION 1.1)
# 生成名为 hello_cmake 的动态库,main.cpp为其对应的源文件,如果要生成静态库,需要将SHARED变为 STATIC
add_library(entry SHARED hello.cpp)
# 添加entry库的依赖,libace_napi.z.so文件位置在
# Library/Huawei/Sdk/openharmony/9/native/sysroot/usr/lib/
target_link_libraries(entry PUBLIC libace_napi.z.so)
5. codelabs 中的XComponent示例
- 通过cmake定义宏
# $ cmake --version
# 设置本脚本支持的最小cmake版本,通过EevEco Studio 创建c++时,默认会自动生成好
cmake_minimum_required(VERSION 3.4.1)
# c++/c 工程的名称
# 这里也可以设置工程版本号,如 project(hello_cmake VERSION 1.0)
project(XComponent)
# 定义变量NATIVERENDER_ROOT_PATH,值来自常量CMAKE_CURRENT_SOURCE_DIR
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
# 定义宏 OHOS_PLATFORM, 即C++/C文件中使用到了#ifdef OHOS_PLATFORM
add_definitions(-DOHOS_PLATFORM)
# 指定头文件夹路径
# 在HarmonyOS 工程中设置头文件路径采用的都是 include_directories, 不会针对每个库进行单独的设置
# 如果想要针对每个库单独设置它所依赖的头文件,需要用到 target_include_directories
include_directories(
${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include
)
# 生成动态库 nativerender
add_library(nativerender SHARED
render/egl_core.cpp
render/plugin_render.cpp
manager/plugin_manager.cpp
napi_init.cpp
)
# 查找EGL库,将其指向变量EGL-lib
# EGL的全称是 libEGL.so,其位置是在Library/Huawei/Sdk/openharmony/9/native/sysroot/usr/lib/
find_library(
# Sets the name of the path variable.
EGL-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
EGL
)
# 查找EGL库,将其指向变量EGLS-lib
# GLESv3的全称是 libGLESv3.so,其位置是在Library/Huawei/Sdk/openharmony/9/native/sysroot/usr/lib/
find_library(
# Sets the name of the path variable.
GLES-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
GLESv3
)
# 查找EGL库,将其指向变量hilog-lib
# hilog_ndk.z的全称是 libhilog_ndk.z.so,其位置是在Library/Huawei/Sdk/openharmony/9/native/sysroot/usr/lib/
find_library(
# Sets the name of the path variable.
hilog-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
hilog_ndk.z
)
......
# 添加 nativerender 库的依赖
# libc++.a 位置 Library/Huawei/Sdk/openharmony/9/native/llvm/lib/aarch64-linux-ohos/c++
target_link_libraries(nativerender PUBLIC
${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libc++.a)
6. 子目录依赖
实践编译命令
- 进入 02-sub-projects/A-basic 目录
- cmake -S . -B .cxx;
- cmake --build .cxx
- .cxx/subbinary/subbinary
主 CMakeLists.txt 文件
# 设置本脚本支持的最小cmake版本
cmake_minimum_required (VERSION 3.5)
# c++/c 工程的名称
project(subprojects)
# 添加子目录
add_subdirectory(sublibrary1)
add_subdirectory(sublibrary2)
add_subdirectory(subbinary)
subbinary目录 CMakeLists.txt
# c++/c 工程的名称
project(subbinary)
# 生成名为 subbinary 的可执行文件,对应的源文件为main.cpp
# 如果是生成库,只需要将add_executable 替换为 add_library
add_executable(${PROJECT_NAME} main.cpp)
# 链接别名为sub::lib1 和 sub::lib2的库
target_link_libraries(${PROJECT_NAME}
sub::lib1
sub::lib2
)
subbinary1目录 CMakeLists.txt
# c++/c 工程的名称
project (sublibrary1)
# 生成 sublibrary1 库
add_library(${PROJECT_NAME} src/sublib1.cpp)
# 为 sublibrary1 库添加别名 sub:: lib1
add_library(sub::lib1 ALIAS ${PROJECT_NAME})
# 为 sublibrary1 库指定头文件见所在文件夹路径
target_include_directories( ${PROJECT_NAME}
PUBLIC ${PROJECT_SOURCE_DIR}/include
)
subbinary2目录 CMakeLists.txt
# c++/c 工程的名称
project (sublibrary2)
# 生成 sublibrary2 库,仅仅包含接口
add_library(${PROJECT_NAME} INTERFACE)
# 为 sublibrary2 库添加别名 sub:: lib2
add_library(sub::lib2 ALIAS ${PROJECT_NAME})
# 为 sublibrary2 库指定头文件见所在文件夹路径
target_include_directories(${PROJECT_NAME}
INTERFACE
${PROJECT_SOURCE_DIR}/include
)
7. 链接三方库
01-basic/H-third-party-library 目录下, 为了可以编译通过, 将例子中的Boost换成ZLIB
# 设置本脚本支持的最小cmake版本
cmake_minimum_required(VERSION 3.5)
# c++/c 工程的名称
project (imported_targets)
# find a boost install with the libraries filesystem and system
#find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)
# 查找已安装的ZLIB
find_package(ZLIB)
# 检查ZLIB是否被找到, <PackageName>_FOUND 是固定格式的变量
if(ZLIB_FOUND)
message ("zlib found")
else()
message (FATAL_ERROR "zlib find Boost")
endif()
# 生成名为 imported_targets 的可执行文件,对应的源文件为main.cpp
add_executable(imported_targets main.cpp)
# 链接ZLIB库
target_link_libraries( imported_targets
PRIVATE
ZLIB
)
8. 增加调试信息
在编写CMakeLists.txt时,也可以输出运行日志
最终会看到:日志 xx/xx/cmake-examples/01-basic/K-imported-targets
message(日志 ${CMAKE_CURRENT_SOURCE_DIR})
9. 链接已存在的动态库
# 设置支持的cmake最低版本号
cmake_minimum_required(VERSION 3.4.1)
#设置工程名称
project(HarmonyLearn)
#设置工程根目录路径变量
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
#指定头文件路径
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
# 生成customnapi动态库,源文件为main.cpp, util.cpp
add_library(customnapi SHARED main.cpp
util.cpp)
#指定 动态库路径
set(my_lib_path ${CMAKE_CURRENT_SOURCE_DIR}/libs/)
#生成 sm4.so
add_library(sm4 SHARED IMPORTED)
set_target_properties(sm4 PROPERTIES IMPORTED_LOCATION ${my_lib_path}${OHOS_ARCH}/libsm4.so)
#链接customnapi动态库
target_link_libraries(customnapi PUBLIC libace_napi.z.so sm4)
10. 批量添加源文件
比如在 add_library(test SHARED xx.cpp yy.cpp)
时,手动比较添加麻烦,快捷的方法
通过快捷命令file,直接将匹配到的文件路径复制给sources变量
file(GLOB sources CONFIGURE_DEPENDS *.cpp *.h)
add_library(test SHARED ${sources})
注意,批量添加带来的方便不一定适用于所有的工程,原因是工程中可能存在一些区分平台或者其它原因的源文件
CMake 语言
上边的CMakeLists.txt文件,简称为CMake文件,其内容编写规则叫做 CMake语言。
CMake语言编写的文件,在工程中以2种形式出现
- CMakeLists.txt 文件
- xxxx.cmake 文件(两种分类:a.脚本 b.模块)
源文件
CMakeLists.txt, xxx.cmake都是CMake的源文件
源文件的内容是零个或多个命令调用组成,命令调用之间采用空格或者换行分离
如下代码中的 cmake_minimum_required
叫做命令调用,看着像函数/方法调用
cmake_minimum_required(VERSION 3.4.1)
注释
# 单行注释
#[[演示多行注释,
注意#号与第一个方括号之间不能有空格]]
控制结构
条件语句
if()… elseif()…else()…endif()
if(<condition>)
<commands>
elseif(<condition>) # 可选, 可以重复出现
<commands>
else() # 可选
<commands>
endif()
循环
- foreach()…endforeach()
- while()…endwhile()
foreach(<loop_var> <items>)
<commands>
endforeach()
while(<condition>)
<commands>
endwhile()
命令定义
- macro()…endmacro()
- function()…endfunction()
macro(<name> [<arg1> ...])
<commands>
endmacro()
function(<name> [<arg1> ...])
<commands>
endfunction()
变量
定义一个变量:set(name value)
删除已定义的变量:unset(name)
如下代码,运行时会报错,报错位置是第5行,报错原因:testvar
变量没有定义。消除错误的方法:注释掉unset(testvar)
set(testvar 1)
message(${testvar})
unset(testvar)
message(${testvar})
变量也是有作用域的(即 局部/全局)
set(testvar 1) #CMakeLists.txt范围内
function(testfun)
set(testvar 2) #函数内
endfunction()
testfun()
message(${testvar})
CMake还提供了大量的公共变量,具体的可参见 [cmake.org/cmake/help/…]
引用这些公共变量的方法:${公共变量名}
message(${CMAKE_CURRENT_SOURCE_DIR})
注意,关于描述平台的公共变量中,目前没有HarmonyOS, 参见:[cmake.org/cmake/help/…]
具有HarmonyOS特征的公共变量,大概有如下几个
- OHOS_ARCH 【CPU架构】
- OHOS_SDK_NATIVE 【SDK对应Native路径】
- CMAKE_OHOS_ARCH_ABI 【CPU架构】
环境变量
获取一个环境变量:$ENV{变量名称}
比如获取系统所有的PATH 环境变量: $ENV{PATH}
可以通过message($ENV{PATH})
将值打印出来
即然是变量,所以也可以设置
set(ENV{MY} TEST)
message($ENV{MY})
最后呢,很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。
而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点
如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。
高清完整版请点击→《鸿蒙NEXT星河版开发学习文档》
针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细资料鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。
《鸿蒙 (OpenHarmony)开发学习视频》
《鸿蒙生态应用开发V2.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
《鸿蒙开发基础》
《鸿蒙开发进阶》
《鸿蒙开发实战》
获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》
总结
鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。
并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!