You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
set(CMAKE_C_FLAGS_DEBUG_INIT "/MTd") set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/MT") set(CMAKE_C_FLAGS_RELEASE_INIT "/MT") set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/MT") Change the default flags for specific config.
在核查cmGlobalVisualStudio14Generator的代码时发现cmake有一个针对sdk版本过滤的逻辑,而visual studio默认未设置版本时是否可以自动设置为10.0呢?是的,VisualStudio会帮我们自动设置为10.0 latest installed version
具体的方式是:
cmake -G "Visual Studio 17 2022" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM=10.0
但是,这样是否有什么后遗症? 目前还未验证,待调试所有参数完毕后测试验证下编译效果
2. 推荐使用至少3.15版本的CMake
CMAKE_MINIMUM_REQUIRED(VERSION 3.15)
3. 用于执行CMake的bat脚本
使用.bat脚本调用cmake,可以指定比较复杂的cmake.exe命令的参数
:: ${ProjectRoot}/build/vs2017-x64.bat
@echo off
::build directory
:: it should be similar name with cmake generator name
set BUILD_DIR=vs2017-x64
:: platform
:: x86 or x64
set BUILD_PLATFORM=x64
:: cl.exe compiler version
set BUILD_COMPILER=v142
:: create directory if not exist
if not exist %BUILD_DIR% md %BUILD_DIR%
cd %BUILD_DIR%
:: run cmake by specifing:
:: - generator
:: - installation directory
:: - CMakeLists.txt location
cmake -G "Visual Studio 12 2017 Win64" -DCMAKE_INSTALL_PREFIX=D:/target/%BUILD_PLATFORM%/%BUILD_COMPILER%
:: run build by specifying config and target
:: note: this may fail, and please open .sln and do manual compilation and installation
cmake --build . --config Release --target INSTALL
:: go back to old folder
cd ..
:: stuck to show build messages
pause
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
#message("inside windows")
# add SAFESEH to Visual Studio. copied from http://www.reactos.org/pipermail/ros-diffs/2010-November/039192.html
#if(${_MACHINE_ARCH_FLAG} MATCHES X86) # fails
#message("inside that branch")
# in VS2013, there is: fatal error LNK1104: cannot open file "LIBC.lib"
# so, we have to add /NODEFAULTLIB:LIBC.LIB
# reference: https://stackoverflow.com/questions/6016649/cannot-open-file-libc-lib
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO /NODEFAULTLIB:LIBC.LIB")
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO /NODEFAULTLIB:LIBC.LIB")
set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO /NODEFAULTLIB:LIBC.LIB")
#endif()
endif (CMAKE_SYSTEM_NAME MATCHES "Windows")
# each time the `demo` target is built, we copy zlib1.dll if it is changed.
add_custom_command(TARGET demo
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${ZLIB_DLL}
${CMAKE_BINARY_DIR}/)
52. 压缩或解压.zip/tar.gz
tar命令是从cmake 3.2开始支持的内置命令,以解压doctest.zip到项目根目录为例:
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/doctest.zip
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
The text was updated successfully, but these errors were encountered:
suhao
changed the title
CMake如何设置设置Visual Studio的WindowsSDKVersion(WindowsTargetPlatformVersion)值为10.0 (latest installed version)
CMake与VisualStudio工程配置映射
Jan 18, 2022
本文将迁移至Rapid C++: CMake与VisualStudio工程配置映射
本文整理了工作中常用的CMake与VisualStudio工程配置的映射关系,便于基于现有VS工程迁移到CMake,以及保持开源项目与现有项目的编译兼容性。
工作中的项目工程使用VisualStudio2019进行编译,而开发机已经升至最新的VisualStudio2022,故而会包含相关版本上的兼容讨论。工程配置以VisualStudioCommunity2022Preview版本为参考。
一、CMake概念与配置
参考CMake Wiki.
1. CMakeList.txt:常规CMake配置文件,配置工程名称、生成选项等,是所有生成所必须的
2. CMAKE_BUILD_TYPE:可枚举值为Debug、Release、RelWithDebInfo、MinSizeRel
3. CMAKE_EXE_LINKER_FLAGS:链接器标志
4. add_dependencies:项目引用Reference,添加依赖
5. source_group( header FILES includeme.h ):Put files into folders
二、Visual Studio与CMake配置映射表
参考Windows C++ project property page reference.
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/Zi")
set_target_properties( target PROPERTIES COMPILE_FLAGS “/clr:pure”)
set_target_properties( target PROPERTIES COMPILE_FLAGS “/clr:safe”)
set_target_properties( target PROPERTIES COMPILE_FLAGS “/clr:oldSynax”)
set_target_properties( target PROPERTIES COMPILE_FLAGS “/W1” )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/W2” )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/W3” )
set_target_properties( target PROPERTIES COMPILE_FLAGS "/W4" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Wall” )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/WX" ) #Yes
#Don’t set means No
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/O1")
set(CMAKE_C_FLAGS_RELEASE_INIT "/O2")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/O2")
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/MT /O1 /Ob1 /D NDEBUG")
set(CMAKE_C_FLAGS_RELEASE_INIT "/MT /O2 /Ob2 /D NDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/MT /Zi /O2 /Ob1 /D NDEBUG")
#Don’t set means no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Ot" ) #speed
#Don’t set means neither
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Oy" ) #yes
#not setting means no
#not setting means no
set_source_files_properties( filename.cpp PROPERTIES COMPILE_DEFINITIONS DEFNAME=DEFVAL )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/GF-" ) #no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Gm-" )#no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/EHa" ) #yes, with SEH exceptions
set_target_properties( target PROPERTIES COMPILE_FLAGS “/EHs" ) #yes, with extern C functions
#not setting means no
not setting means no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/RTCu" ) #Uninitialized Variable
set_target_properties( target PROPERTIES COMPILE_FLAGS “/TRC1" ) #Both
#not setting means no
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/MT")
set(CMAKE_C_FLAGS_RELEASE_INIT "/MT")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/MT")
Change the default flags for specific config.
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Zp2" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Zp4" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Zp8" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Zp16" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/GS-" ) #no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Gy-" ) #no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/fp:strict" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/fp:fast" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Zc:wchar_t-" ) #no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/GR-" ) #no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/openmp-" )#no
set_target_properties( target PROPERTIES COMPILE_FLAGS "/Yu" ) #use
set_target_properties( target PROPERTIES COMPILE_FLAGS "/Yustdafx.h" )
set_target_properties( target PROPERTIES COMPILE_FLAGS "/FAc" )
set_target_properties( target PROPERTIES COMPILE_FLAGS "/FAs" )
set_target_properties( target PROPERTIES COMPILE_FLAGS "/FAcs" )
#not setting means no list
#Don’t set means no
set_target_properties( target PROPERTIES COMPILE_FLAGS "/Gr" ) #_fastcall
set_target_properties( target PROPERTIES COMPILE_FLAGS "/Gz" ) #_stdcall
set_target_properties( target PROPERTIES LINKER_LANGUAGE "C" ) #C
or
set_target_properties( target PROPERTIES COMPILE_FLAGS "/TP" ) #CXX
set_target_properties( target PROPERTIES COMPILE_FLAGS "/TC" ) #C
set_target_properties( target PROPERTIES COMPILE_FLAGS "/errorReport:none" )
set_target_properties( target PROPERTIES COMPILE_FLAGS "/errorReport:prompt" )
set_target_properties( target PROPERTIES COMPILE_FLAGS "/errorReport:send" )
set_target_properties( target PROPERTIES OUTPUT_NAME "Helloworld" )
set_target_properties( target PROPERTIES PREFIX "lib" )
set_target_properties( target PROPERTIES SUFFIX "lib" )
#for debug version
set_target_properties( target PROPERTIES DEBUG_OUTPUT_NAME "Helloworld" )
set_target_properties( target PROPERTIES DEBUG_PREFIX "lib" )
set_target_properties( target PROPERTIES DEBUG_SUFFIX "lib" )
#For dlls
set_target_properties( target PROPERTIES OUTPUT_NAME "Helloworld" )
set_target_properties( target PROPERTIES IMPORT_PREFIX "lib" )
set_target_properties( target PROPERTIES IMPORT_SUFFIX "lib" )
set_target_properties( target PROPERTIES PREFIX "bin" )
set_target_properties( target PROPERTIES SUFFIX "dll" )
set_target_properties( target PROPERTIES LINK_FLAGS "/VERBOSE:Lib" )
set_target_properties( target PROPERTIES LINK_FLAGS "/VERBOSE:ICF" )
set_target_properties( target PROPERTIES LINK_FLAGS "/VERBOSE:REF" )
set_target_properties( target PROPERTIES LINK_FLAGS "/VERBOSE:SAFESEH" )
set_target_properties( target PROPERTIES LINK_FLAGS "/VERBOSE:CLR" )
set_target_properties( target PROPERTIES LINK_FLAGS "/INCREMENTAL:NO" )
set( CMAKE_EXE_LINKER_FLAGS_DEBUG "/INCREMENTAL" )
set( CMAKE_EXE_LINKER_FLAGS_DEBUG "/INCREMENTAL:NO" )
set_target_properties( target PROPERTIES LINK_FLAGS "/LIBPATH:dir1 /LIBPATH:dir2" )
set_target_properties( target PROPERTIES LINK_FLAGS "/LTCG:NOSTATUS" )
set_target_properties( target PROPERTIES LINK_FLAGS "/ALLOWBIND:YES" )
set_target_properties( target PROPERTIES LINK_FLAGS "/FUNCTIONPADMIN:16" ) #Itanium only
set_target_properties( target PROPERTIES LINK_FLAGS "/FUNCTIONPADMIN:6" ) #x64 only
set_target_properties( target PROPERTIES LINK_FLAGS "/FUNCTIONPADMIN:5" ) #x86 only
三、工程配置需求的非官方非标准的解决方案
1. 设置Visual Studio的WindowsSDKVersion(WindowsTargetPlatformVersion)值为10.0 (latest installed version)
在使用cmake生成的visual studio工程中,WIndowsSDKVersion总是为本机的最新SDK版本号,对于想要控制版本号或者使用最新版本均十分不便。
查阅了现有文档,以及官方文档均无相关配置项。
可参考官方相关讨论:https://gitlab.kitware.com/cmake/cmake/-/issues/21403
在核查cmGlobalVisualStudio14Generator的代码时发现cmake有一个针对sdk版本过滤的逻辑,而visual studio默认未设置版本时是否可以自动设置为10.0呢?是的,VisualStudio会帮我们自动设置为10.0 latest installed version
具体的方式是:
cmake -G "Visual Studio 17 2022" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM=10.0
但是,这样是否有什么后遗症? 目前还未验证,待调试所有参数完毕后测试验证下编译效果
2. 推荐使用至少3.15版本的CMake
3. 用于执行CMake的bat脚本
使用.bat脚本调用cmake,可以指定比较复杂的cmake.exe命令的参数
4. 判断平台:32位、64位
方法1:CMAKE_SIZEOF_VOID_P 表示 void* 的大小(例如为 4 或者 8),可以使用其来判断当前构建为 32 位还是 64 位,CMake官方推荐
方法2:判断**CMAKE_CL_64** 是否为true,CMake官方已废弃,此判断仅且仅当使用cl.exe时有效
5. 判断Visual Studio版本:MSVC_VERSION
6. 判断操作系统:其中WIN32判断的是windows系统,包括32位和64位两种情况
测试发现,如果在CMAKE_MINIMUM_VERSION()后立即使用CMAKE_SYSTEM_NAME,Linux下得到结果为空,Android下得到为Android。看起来是Android的toolchain中进行了设定。
7. 判断Debug/Release
8. 根据Debug/Release添加不同的库目录
在vs平台下,会自动把path和path/$(Configuration)添加到库搜索目录。
9. 设定编译选项
修改CMAKE_C_FLAGS、CMAKE_CXX_FLAGS变量
10. SAFESEH报错
解决办法是:
11. link_directory但是链接异常
link_directories() 这句话必须在add_executable()之前写 不然找不到库目录
或者先add_executable() 再 target_link_directories(XXX PRIVATE some direcotry)
12. Debug库带“d”后缀
设置debug模式下编译出的库文件,相比于release模式下,多带一个字母"d"作为后缀。
13. 在cmake中执行目录创建、拷贝文件等脚本:add_custom_command、execute_process
创建目录:file(MAKE_DIRECTORY ${SO_OUTPUT_PATH})
和某个target绑定的文件拷贝,使用add_custom_command:add_custom_command(TARGET your_target PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${MY_SO_NAME} ${SO_OUTPUT_PATH}/)
和target无关的,或者说对于所有target而言都需要做文件拷贝,用execute_process
14. 转换相对路径为绝对路径
get_filename_component(SO_OUTPUT_PATH_ABS ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${ANDROID_ABI} ABSOLUTE)
15. 循环处理列表:cmake中的列表也是字符串,不过,通过list(APPEND)得到的列表字符串,可以用foreach来遍历其中每个字符串
16. 设置C/C++编译器
通过设定CMAKE_C_COMPILER和CMAKE_CXX_COMPILER来做到。
注意:project()命令必须在设定编译器之后出现,否则编译器的设定不起作用,将使用系统默认编译器。
注:前一种方法是在单个CMakeLists.txt中设定。对于跨平台编译,则应当避免污染根CMakeLists.txt,应该为每个平台分别使用cmake cache script。而在cache script中需要设定的变量,都应该是缓存变量。
17. 设定导入库(IMPORTED)及其属性
如果是自己项目中的源码基于cmake构建,其中利用add_library()创建的库目标,可以直接用来作为可执行目标、动态库或静态库的依赖库直接使用。
而如果是别人直接丢过来的库和头文件、没有用cmake封装一次呢?显然我们不应该在Visual Studio的项目属性中手动添加,手写一个导入库的cmake,在add_library()命令中指定关键字IMPORTED,再用set_target_properties()命令来设定导入库目标的头文件目录、库目录、库文件名字:
其中GLOBAL关键字,是为了让全局可见。例如通过add_subdirectory()添加了mpbase库,里面是上述方式添加的库,但是上级CMakeLists.txt要确保能使用这个库,就需要指定GLOBAL关键字。
P.S. 实践发现,如果库文件所在目录很长(超过256个字符),或者添加的导入库对应的库文件有多个,它们的名字会被拼接起来,在CMake+Ninja的NDK开发环境下直接报错说路径太长。因此,导入库并不是一个好的实践。
18. 查看并修改Visual Studio项目属性中的某个设定
问题来自StackOverFlow上某网友的提问:Compile error CMAKE with CUDA on Visual Studio C++
解决步骤:
19. 添加宏定义
相当于传递给C/C++编译器:
20. 设置fPIE
问题出现在:连接一个静态库到一个可执行程序,并在android6.0上运行
解决办法:
21. 设置fPIC
问题出现场景:编译动态库libaisf_bodyattr_processor.so的时候,它依赖于静态库libarcsoft_bsd.a,但是libarcsoft_bsd.a库编译时没有指定fPIC编译选项。
在编译静态库的时候,全局设定:set(CMAKE_POSITION_INDEPENDENT_CODE ON)
在编译静态库的时候,设定
22. Linux gcc添加链接库"-lm":target_link_libraries(xxx m)
23. 清空普通变量:unset()
24. 清除缓存变量:unset( CACHE)
25. FindXXX.cmake简单例子
使用场景满足的条件:
此时如果继续在CMakeLists.txt中“一把梭”,各种设定都写在单个文件中,可以执行就不够高了。每个依赖写成FindXXX.cmake,则后续直接使用find_package(XXX)很方便,定位排查依赖项问题、可移植性都得到了增强。
FindXXX.cmake基本步骤
例子1:单个头文件和单个库文件
例子2:同时存在Debug和Release版本的库
希望在调用find_package(xxx)之后,Visual Studio或XCode等IDE能自动切换debug和release的库。则需要为debug库的路径添加debug字段,为release库添加optimized字段。
考虑到硬编码不是一个好的方案,库文件可能放在lib、lib64、lib/Release等目录中,应当先用find_library()进行查找,然后再set库文件变量LEMON_LIBRARIES。
多个库的find_library写法
对于依赖库中的多个库,自然的想法是使用foreach()来处理每个库文件。
考虑到find_library(lemon_lib_name)会产生缓存变量lemon_lib_name,这会导致再次调用find_library(lemon_lib_name)时不再查找。需要unset(${lemon_lib_name} CACHE)该缓存变量来确保查找成功。直接给出完整例子:
例子3:找dll
注意:CMAKE_FIND_LIBRARY_SUFFIXES的使用:CMake find_library matching behavior?
实际上,CMAKE_FIND_LIBRARY_SUFFIXES影响最大的就是find_library()命令了。譬如zlib安装目录下,同时存在动态库和静态库,分别是libz.a和libz.so,而find_library()的行为是“找到一个就不再找了”,因此如果没有很好的设定CMAKE_FIND_LIBRARY_SUFFIXES,就会导致找不到想要的库。默认情况下是找到动态库,然而windows下还需要手动拷贝DLL。。。麻烦。
可以通过备份原有的CMAKE_FIND_LIBRARY_SUFFIXES的值,改掉它的值,find_library()之后再改回原来的值,这样就支持了 静态库/动态库 分别查找的设定。。(用于魔改cmake自带的FindZLIB.cmake)
CMake find module to distinguish shared or static library
26. CMake各种编译链接参数的默认值
27. 链接器相关问题
检查链接到的重名函数
场景:A库的代码中定义了函数play(),B库的代码中也定义了函数play(),但是这两个play()函数的实现不同,并且被可执行目标C同时链接。
链接器默认是找到一个符号就不再查找,因此默认能链接并且可以运行,只不过运行结果不是所期待的。
容易查到,Linux下gcc对应的链接器中可以使用--whole-archive和--no-whole-archive参数来包含静态库中的所有符号。
如果是gcc,则使用gcc -Wl --whole-archive someLib --no-whole-archive。
如果是Visual Studio,则需要>=2015 update2的版本中才支持/WHOLEARCHIVE选项,VS2013要哭泣了。
因而,在CMakeLists.txt中,可以设定链接器的全局设定:
缺点:
TODO: 对于单个target,如何设定?
或者:
实际上:
gcc的链接器ld:通过-Wl, --whole-archive lib_name -Wl, --no-whole-archive能每次分别对一个静态库加载所有的member(函数等
Visual Studio的链接器:VS2017(1900)之后才支持-WHOLEARCHIVE来实现同样功能;
PC Clang的链接器:使用lld作为链接器(比如Xcode现在用肯定是lld),用-Wl,-force_load ${lib}来做到只导入一个静态库中的所有member
NDK的链接器:怎么说现在用的NDK也是17b起步了,默认编译器是Clang,gcc暂时没考虑;虽然编译器很早就(可以)切换到Clang了,但链接器目前还是用的gcc的ld,因此NDK的链接阶段检查重复符号应该用ld的检查方式;即使是用当前(2020-01-26 02:16:34)最新的NDK也就是NDK21,手动传入ANDROID_LD=lld后,NDK切换到的链接器lld和MacOS上与AppleClang搭配的lld也还是不一样,链接阶段查重复符号仍然需要传gcc的ld的那一套参数:-Wl, --whole-archive lib_name -Wl, --no-whole-archive,但好处是报错界面更加友好直观了:

gcc和ld 中的参数 --whole-archive 和 --no-whole-archive
lld中的-force_load参数功能说明是在邮件列表中找到的,官方文档里还更新出来
ndk-20的change log
28. 生成compile_commands.json:用于在VSCode等编辑器/IDE中给C/C++代码做函数定义跳转支持
29. 子目录CMakeLists.txt中产生变量给父目录中的CMakeLists.txt使用
set设定变量并且设定PARENT_SCOPE参数。当项目中代码变多,就可能需要分成多个目录存放。每个目录下放一个CMakeLists.txt,写出它要处理的文件列表,然后暴露给外层CMakeLists.txt,使外层CMakeLists.txt保持清爽结构。
30. 在IDE中将targets分组显示:使用folder
包括两步:
31. 设置Debug的优化级别参数
32. cmake生成VS2019的工程
VS2019开始,需要用-A参数指定是32位程序,还是64位程序。以前的用法不行了
33. 不要同时使用include_directories()和target_include_directories()
用法有坑,其中target_include_directories()设置的目录不会生效。经验:只使用其中一种设定include目录的方式。
34. NDK开发中出现error: cannot use 'try' with exceptions disabled
这需要编译器的编译选项中开启exception的支持。现在NDK开发,主流的做法是Android Studio + gradle + cmake做构建,需要在build.gradle中设定-fexceptions:
但有时候,发现上述设定后并不生效;尝试删除.externalBuild目录重新构建,仍然报exception无法处理。后来发现,问题出在CMakeLists.txt加载的.cmake脚本中(用的代码框架是其它部门同事写的,所以不是很熟悉),他给手动设定了这么一句:
去掉-fno-exception即可。
35. cmake-gui中可见(可检索)的变量
目前遇到的有两种:
当然,还可以使用mark_as_adances来设定,则默认在cmake-gui中不可见
36. Ninja error: Filename longer than 260 characters
NDK开发中,CMake+Ninja构建,如果文件名超过260个字符会失败。这个限制略蛋疼
37. cmake判断C/C++编译器
比如我想要定制一些安全的编译选项,发现需要区分msvc,gcc和clang。容易直接想到CMAKE_C_COMPILER和CMAKE_CXX_COMPILER。但考虑到-G Xcode和命令行下分别输出,得到的结果并不都是clang,这就很蛋疼。
使用CMAKE_CXX_COMPILER_ID比较方便,像上面提到的case会做合并输出AppleClang。而如果是NDK-r17c则输出Clang。
常见的平台下对应输出:
更多结果看官方文档 CMAKE__COMPILER_ID
38. cmake字符串追加元素,并且用空格分隔
简单有效的两种方式:
39. cmake --build的使用:cmake执行编译链接、安装
本质上,当使用cmake ..,或cmake -G "Visual Studio 15 2017 Win64" ../..类似命令时,是执行“pre-make”,相当于是makefile的生成器,可以说对于你的项目代码来说并没执行编译链接,更没有安装。
实际使用经验:cmake生成了这些cache文件后,可以打开Visual Studio编译,或执行make编译(Linux下)。但这些都是native tool。通用的方式则是用cmake包装好的接口:
40. C/C++和汇编混编
在cmake里混合编译C/C++与汇编代码,通过enable_language(ASM_)可以做到。
例如x86(32位)的MASM(Visual Studio支持的一种汇编语法):enable_language(ASM_MASM)
此外往往还需要给Visual Studio设置SEH:
汇编文件则和C/C++文件一起,正常添加为target的依赖即可:add_executable(run src/main.cpp src/CalcSum_.asm)
41. cmake设置pthread
42. cmake使用pkg-config
有些依赖库只提供.pc文件,甚至已经配置了CMake脚本但是安装后还是只有.pc而没有XXXConfig.cmake或xxx-config.cmake。并且不仅是Linux,Windows上也这样。
这就不得不在CMake中尝试去加载.pc文件。原理是,cmake里面封装了对pkg-config工具的兼容,可以认为是一个插件,用这个插件去加载.pc文件。实际测试发现Linux和Windows都可以用。
使用:先找到xx.pc文件,然后分成目录和文件前缀两部分,在cmake中配置
举例1:Ubuntu 16.04下用apt安装openblas并在CMake中用pkg-config方式配置:
举例2:Windows 10下用CMake配置Pangolin安装中配置的zlib
43. cmake多行注释
44. 命令行-D指定参数
在cmake脚本中指定其中任意一种:
45. include()指令
CMAKE_MODULE_PATH是在CMake脚本中用户可以自行修改的变量。
CMAKE MODULE DIRECTORY是什么,官方文档没明确说。其实说的应该是cmake安装后的Modules目录,例如/usr/local/share/cmake/Modules
46. list追加元素,或list首部插入元素
47. cmake中使用IWYU
IWYU 是 google 的开源项目,用来移除不必要的头文件。
48. target_link_libraries()
cmake 3.13 开始提供的命令。低版本cmake无法使用
49. CMake构建NDK项目提示asm/types.h找不到
用CMake构建NDK项目时,会传入toolchain的cmake脚本文件android.toolchain.cmake给CMake。这个文件中会做若干设定,其中就包括include路径。
我遇到的情况是,自己手动修改CMAKE_C_FLAGS和CMAKE_CXX_FLAGS时,覆盖了它们原有的(android.toolchain.cmake修改后的)值,导致asm/types.h找不到。
P.S. 排查方法:由于我是基于ninja构建的(cmake+ndk的组合下,现在通常用ninja),通过对比”能正常构建的工程“和”提示asm/types.h找不到的工程“之间${CMAKE_BINARY_DIR}目录下的rules.ninja和build.ninja来发现问题所在。
50. windows下创建的共享库,没生成.lib文件
.lib是导入库,里面存访对外可见(暴露)的符号(函数、变量)。.dll应该搭配一个.lib导入库才能使用。
如果是自己的源码生成的dll共享库,则在CMakeLists.txt一开始,添加:set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)则可以导出所有的符号。
而如果只想导出一部分符号,则可以为每个函数分别指定导出规则。
51. 拷贝dll
在Windows下,Visual Studio中,如果用了动态库(例如opencv、zlib等),需要把dll放到PATH环境变量中,使得运行时能找到dll。
而其实Windows下的PATH查找,是会在CMAKE_BINARY_DIR目录下查找的。如果不想改PATH环境变量,也不希望每次都要手动拷贝dll,包括清掉build目录后重新构建时也不想手动拷贝,那么可以用cmake命令来搞。
举个例子,调用zlib库执行文本压缩解压,用到了zlib1.dll,其中executable target名字是demo。
zlib的二进制包下载:https://nsis.sourceforge.io/mediawiki/images/b/bb/Zlib-1.2.8-win64-AMD64.zip
zlib的调用示例代码:https://blog.csdn.net/yuhuqiao/article/details/82188963
cmake中拷贝zlib1.dll的写法:
52. 压缩或解压.zip/tar.gz
tar命令是从cmake 3.2开始支持的内置命令,以解压doctest.zip到项目根目录为例:
https://stackoverflow.com/questions/48891928/cmake-unzip-automaticallywhile-building-when-the-zip-file-changes
53. 列出所有target
有些基于cmake的项目,CMakeLists.txt写的很复杂很庞大。可以列出所有target,帮助理清思路。
列出makefile中的所有target:
实际上,makefile里诸如"all"和"clean"这样的target,并不是我们感兴趣的。还是shell大法拼凑一下吧:
54. 判断文件是否存在
EXISTS可以判断,同时适用于文件和目录。
举例:查找当前目录下是否存在doctest目录,并且检查doctest目录下是否存在doctest_fwd.h和doctest.cpp文件:
55. 打印变量
56. 判断是否为目录
用IS_DIRECTORY命令判断。例如shadow中判断各个backend,如果是目录,则添加subdirectory,写法如下:
57. Visual Studio环境下的MT,MTd, MD, MDd的设定
https://stackoverflow.com/a/56490614/2999096
从cmake3.15开始,可以用CMAKE_MSVC_RUNTIME_LIBRARY和MSVC_RUNTIME_LIBRARY设定。
可以全局设定:set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDebug)
针对单个目标设定:
候选值有4种:
58. 设定Address Sanitizer(ASAN)
首先确保有符号信息(例如CMAKE_BUILD_TYPE设定为Debug)。
其次是设定如下几个变量中的其中一个(多个也行但没必要,看你需求):
例如我编译的是可执行目标,那么:
对于可执行目标,并且依赖于静态库或动态库,懒人用法:
注意:ASAN似乎对vector等容器的支持不够好。对于vector,预先分配多少内存,似乎ASAN并不知道,导致vector被clear后再使用,(做下标访问一段时间后)出现的segfault,没被ASAN检测到。
59. Linux下编译32位程序
60. 设置VS的可执行文件生成目录
假设.sln目录在 build/vs2019-x64 下,则默认的可执行文件生成目录是 build/vs2019-x64/Debug 或 build/vs2019-x64/Release;而如果文件放在这一默认生成目录下的话无法被读取到(不加前缀的情况),需要放在 build/vs2019-x64 目录下才能读到。
以至于,代码里经常要根据 _MSC_VER 或者 ANDROID 等平台相关的宏,设定不同的路径,例如:
这让代码不整洁,也增加了出错的可能。
可以通过上面两截图中的方式,在项目属性中修改可执行文件的生成目录和启动目录,但仍然不方便;最好的办法还是在 CMakeLists.txt 里设定:
https://stackoverflow.com/questions/47175912/using-cmake-how-to-stop-the-debug-and-release-subdirectories
https://stackoverflow.com/questions/41864259/how-to-set-working-directory-for-visual-studio-2017-rc-cmake-project
61. find_package等的debug输出
从cmake3.17开始,文档里正式说明支持CMAKE_FIND_DEBUG_MODE这一cmake变量,设定为TRUE则打印find_package/find_program/find_file等函数的打印过程
实际上据网友反馈,稍早的版本也可以用这一变量,只不过文档里当时没写。
另外,设定CMAKE_FIND_DEBUG_MODE变量为TRUE,等价于调用cmake时候指定--debug-find参数。
如果你是cmake-GUI方式构建,菜单栏也可以选择输出debug信息:
62. 命令行方式传入option的坑
场景:命令行方式调用cmake,指定了很多option和cache variable的值,希望把这些option和cache variable放在.txt文件中,然后通过cat option.txt方式传给cmake。
结论:option.txt里的写法,-D之后不能有空格,否则无法生效。
例如,正确写法是:
错误写法是
可以用如下 CMakeLists.txt 验证结论:
63. PowerShell中从txt读内容传给cmake
powershell是基于ksh语法修改而来(而不是M的别的其他的语法);‘(cat options.txt)`在bash里也能用;
两个``符号,英文正式名字叫做backtick,而不是"reverse quote"。
99. 现代CMake
常用主要有如下几个不响应target的全局设定:
target_compile_definitions(): 目标添加编译器编译选项,例如target_compile_definitions(shadow_jni PRIVATE -DUSE_STB -DUSE_ARM)
target_include_directories():目标添加包含文件,例如target_include_directories(shadow_jni PRIVATE "shadow_jni/body_detection" "shadow_jni/util" ${SNPE_INC_DIR})
target_link_directories():目标添加链接库查找目录,例如target_link_directories(shadow_jni PRIVATE ${SNPE_LIB_DIR})
target_link_libraries():目标添加链接库,例如target_link_libraries(shadow_jni ${log-lib} ${graph-lib} ${SNPE_LIB})
https://stackoverflow.com/questions/66485987/what-is-the-bash-reverse-quote-equivalent-in-powershell
https://stackoverflow.com/questions/434038/whats-the-cmd-powershell-equivalent-of-back-tick-on-bash
Why is $(...) preferred over ... (backticks)?
参考
The text was updated successfully, but these errors were encountered: