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})
本文将迁移至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 “/arch:SSE2" ) |Floating Point Model | | set_target_properties( target PROPERTIES COMPILE_FLAGS “/fp:precise" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/fp:strict" )
set_target_properties( target PROPERTIES COMPILE_FLAGS “/fp:fast" ) |Enable Floating Point Exceptions | | set_target_properties( target PROPERTIES COMPILE_FLAGS “/fp:except" ) |Create Hotpatchable Image | | set_target_properties( target PROPERTIES COMPILE_FLAGS “/hotpatch" ) |Spectre Mitigation | |Enable Intel JCC Erratum Mitigation | |Enable EH Continuation Metadata | |Enable Signed Returns | |C/C++=>Language | | | 语言 |Disable Language Extensions | | set_target_properties( target PROPERTIES COMPILE_FLAGS “/Za" ) |Conformance mode | |Treat wchar_t As Built in Type | | set_target_properties( target PROPERTIES COMPILE_FLAGS “/Zc:wchar_t" )#yes
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Zc:wchar_t-" ) #no |Force Conformance in For Loop Scope| |Remove unreferenced code and data| |Enforce type conversion rules | |Enable Run-Time Type Information | | set_target_properties( target PROPERTIES COMPILE_FLAGS “/GR" ) #yes
set_target_properties( target PROPERTIES COMPILE_FLAGS “/GR-" ) #no |Open MP Support | | set_target_properties( target PROPERTIES COMPILE_FLAGS “/openmp" )#yes
set_target_properties( target PROPERTIES COMPILE_FLAGS “/openmp-" )#no |C++ Language Standard | |C Language Standard | |Enable Experimental C++ Standard Library Modules| |C/C++=>Precompiled Headers| | | 预编译头 |Create/Use Precompiled Header | Not Using Precompiled Headers | set_target_properties( target PROPERTIES COMPILE_FLAGS "/Yc" ) #create
set_target_properties( target PROPERTIES COMPILE_FLAGS "/Yu" ) #use
not setting means no | 关闭预编译头
|Precompiled Header File | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/Ycstdafx.h" )
set_target_properties( target PROPERTIES COMPILE_FLAGS "/Yustdafx.h" ) |Precompiled Header Output File | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FpPathAndName.pch" ) |C/C++=>Output Files | | | 输出文件 |Expand Attributed Source | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/Fx" ) |Assembler Output | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FA" )
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 |Use Unicode For Assembler Listing| | set_target_properties( target PROPERTIES COMPILE_FLAGS “/FAu" ) #yes
#Don’t set means no |ASM List Location | |Module Output File Name | |Module Dependencies File Name | |Object File Name | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FoName.obj" ) |Program Database File Name | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FdC:/Debug/good.pdb" ) |Generate XML Documentation Files| | set_target_properties( target PROPERTIES COMPILE_FLAGS "/doc" ) |XML Documentation File Name | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/docDocument.xml" ) |Generate Source Dependencies File| |Source Dependencies File Name | |C/C++=>Browse Information | | | 浏览信息 |Enable Browse Information | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FR" ) |Browse Information File | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FRfilename" ) |C/C++=>External Includes | |Treat Files Included with Angle Brackets as External| |External Header Warning Level | |Template Diagnostics in External Headers| |Disable Code Analysis for External Headers| |Analysis Ruleset for External Headers| |C/C++=>Advanced | | | 高级 |Calling Convention | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/Gd" ) #_cdecl
set_target_properties( target PROPERTIES COMPILE_FLAGS "/Gr" ) #_fastcall
set_target_properties( target PROPERTIES COMPILE_FLAGS "/Gz" ) #_stdcall |Compile As | | set_target_properties( target PROPERTIES LINKER_LANGUAGE "CXX" ) #C++
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 |Disable Specific Warnings | %(DisableSpecificWarnings) | set_target_properties( target PROPERTIES COMPILE_FLAGS "/wd4710" ) |Forced Include File | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FIinclude.h" ) |Forced #using File | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FUname" ) |Show Includes | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/showIncludes" ) |Use Full Paths | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/FC" ) |Omit Default Library Name | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/ZI" ) |Internal Compiler Error Reporting | | set_target_properties( target PROPERTIES COMPILE_FLAGS "/errorReport:queue" )
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" ) |Treat Specific Warnings As Errors | 4172;%(TreatSpecificWarningsAsErrors) |C/C++=>Command Line | | | 命令行 |Additional Options | /Zc:threadSafeInit- | | 线程安全初始化 |Librarian=>General | | | 静态库通用设置 |Output File | |Additional Dependencies | |Additional Library Directories | |Suppress Startup Banner | |Module Definition File Name | |Ignore All Default Libraries | |Export Named Functions | |Force Symbol References | |Use Unicode Response Files | |Link Library Dependencies | |Error Reporting | |Treat Lib Warning As Errors | |Target Machine | |SubSystem | |Minimum Required Version | |Remove Objects | |Verbose | |Name | |Link Time Code Generation | |Librarian=>Command Line | | | 静态库命令行 |Additional Options | |Linker=>General | | | 动态库链接通用设置 |Output File | | #normal case
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" ) |Show Progress | | set_target_properties( target PROPERTIES LINK_FLAGS "/VERBOSE" )
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" ) |Version | | set_target_properties( target PROPERTIES VERSION 0.1.2.3) |Enable Incremental Linking | No (/INCREMENTAL:NO) | set_target_properties( target PROPERTIES LINK_FLAGS "/INCREMENTAL" )
set_target_properties( target PROPERTIES LINK_FLAGS "/INCREMENTAL:NO" )
set( CMAKE_EXE_LINKER_FLAGS_DEBUG "/INCREMENTAL" )
set( CMAKE_EXE_LINKER_FLAGS_DEBUG "/INCREMENTAL:NO" ) | 增量链接 |Incremental Link Database File | |Suppress Startup Banner | | set_target_properties( target PROPERTIES LINK_FLAGS "/NOLOGO" ) |Ignore Import Library | |Register Output | |Per-user Redirection | |Additional Library Directories | %(AdditionalLibraryDirectories) | link_directories( dir1 dir2 )
set_target_properties( target PROPERTIES LINK_FLAGS "/LIBPATH:dir1 /LIBPATH:dir2" ) |Link Library Dependencies | |Use Library Dependency Inputs| |Link Status | | set_target_properties( target PROPERTIES LINK_FLAGS "/LTCG:STATUS" )
set_target_properties( target PROPERTIES LINK_FLAGS "/LTCG:NOSTATUS" ) |Prevent DLL Binding | | set_target_properties( target PROPERTIES LINK_FLAGS "/ALLOWBIND:NO" )
set_target_properties( target PROPERTIES LINK_FLAGS "/ALLOWBIND:YES" ) |Treat Linker Warning As Errors | | set_target_properties( target PROPERTIES LINK_FLAGS "/WX" ) |Force File Output | | set_target_properties( target PROPERTIES LINK_FLAGS "/FORCE" ) |Create Hot Patchable Image | | set_target_properties( target PROPERTIES LINK_FLAGS "/FUNCTIONPADMIN" )
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 |Specify Section Attributes | |Linker=>Input | | | 动态库链接输入 |Additional Dependencies | %(AdditionalDependencies) | target_link_libraries( target item1 item2 ) |Ignore All Default Libraries | | set_target_properties( target PROPERTIES LINK_FLAGS "/NODEFAULTLIB" ) |Ignore Specific Default Libraries| |Module Definition File | |Add Module to Assembly | |Embed Managed Resource File| |Force Symbol References | |Delay Loaded DLLs | %(DelayLoadDLLs) |Assembly Link Resource | |Linker=>Manifest File | | | 动态库链接清单文件 |Generate Manifest | |Manifest File | |Additional Manifest Dependencies| |Allow Isolation | |Enable User Account Control (UAC)| |UAC Execution Level | |UAC Bypass UI Protection | |Linker=>Debugging | | | 动态库链接调试 |Generate Debug Info | Generate Debug Information (/DEBUG) | | 输出调试信息,生成pdb |Generate Program Database File| |Strip Private Symbols | |Generate Map File | Yes (/MAP) | | 生成map文件 |Map File Name | |Map Exports | |Debuggable Assembly | |Linker=>System | | | 动态库链接系统 |SubSystem | |Minimum Required Version | |Heap Reserve Size | |Heap Commit Size | |Stack Reserve Size | |Stack Commit Size | |Enable Large Addresses | |Terminal Server | |Swap Run From CD | |Swap Run From Network | |Driver | |Linker=>Optimization | | | 动态库链接优化 |References | Yes (/OPT:REF) |Enable COMDAT Folding | Yes (/OPT:ICF) |Function Order | |Profile Guided Database | |Link Time Code Generation | |Link Time Code Generation Object File| |Linker=>Embedded IDL | |MIDL Commands | |Ignore Embedded IDL | |Merged IDL Base File Name | |Type Library | |TypeLib Resource ID | |Linker=>Windows Metadata| | | 动态库链接元数据 |Generate Windows Metadata| |Windows Metadata File | |Windows Metadata Key File | |Windows Metadata Key Container| |Windows Metadata Delay Sign| |Linker=>Advanced | | | 动态库链接高级 |Entry Point | |No Entry Point | |Set Checksum | |Base Address | |Randomized Base Address | |Fixed Base Address | |Data Execution Prevention (DEP)| |Turn Off Assembly Generation| |Unload delay loaded DLL | |Nobind delay loaded DLL | |Import Library | $(OutDir)$(TargetName).lib |Merge Sections | |Target Machine | |Profile | |CLR Thread Attribute | |CLR Image Type | |Key File | |Key Container | |Delay Sign | |CLR Unmanaged Code Check| |Error Reporting | |SectionAlignment | |Preserve Last Error Code for PInvoke Calls| |CET Shadow Stack Compatible| |Image Has Safe Exception Handlers| |Linker=>Command Line| | | 动态库链接命令行 |Additional Options| |Manifest Tool=>General| | | 清单工具通用配置 |Suppress Startup Banner | |Verbose Output | |Assembly Identity | |Manifest Tool=>Input and Output| | | 清单工具输入输出 |Additional Manifest Files | |Input Resource Manifests | |Embed Manifest | |Output Manifest File | |Manifest Resource File | |Generate Catalog Files | |Generate Manifest From ManagedAssembly| |Suppress Dependency Element| |Generate Category Tags | |DPI Awareness | Per Monitor High DPI Aware |Manifest Tool=>Isolated COM| |Type Library File | |Registrar Script File | |Component File Name | |Replacements File | |Manifest Tool=>Advanced| |Update File Hashes | |Update File Hashes Search Path| |Manifest Tool=>Command Line| |Additional Options | |Resources=>General | |Preprocessor Definitions | |Undefine Preprocessor Definitions| |Culture | |Additional Include Directories| |Ignore Standard Include Paths| |Show Progress | |Suppress Startup Banner | |Resource File Name | |Null Terminate Strings | |Resources=>Command Line| |Additional Options| |XML Data Generator=>General| |Suppress Startup Banner | |Additional Document File | |Output Document File | |Document Library Dependencies| |XML Data Generator=>Command Line| |Additional Options | |Browse Information=>General| |Suppress Startup Banner | |Output File | |Preserve SBR Files | |Browse Information=>Command Line| |Additional Options | |Build Events=>Pre-Build Event| |Command Line | |Description | |Use In Build | |Build Events=>Pre-Link Event| |Command Line | |Description | |Use In Build | |Build Events=>Post-Build Event| |Command Line | |Description | |Use In Build | |Custom Build Step=>General| |Command Line | |Description | |Outputs | |Additional Dependencies | |Treat Output As Content | |Content Root Folder | |Execute After | |Execute Before |
三、工程配置需求的非官方非标准的解决方案
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_VOIDP 表示 void* 的大小(例如为 4 或者 8),可以使用其来判断当前构建为 32 位还是 64 位,CMake官方推荐
方法2:判断_CMAKE_CL64 是否为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,但好处是报错界面更加友好直观了:![image](https://user-images.githubusercontent.com/24261214/150341041-abd21b76-b9be-4277-8e2c-a8ff08da2ccc.png)
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++与汇编代码,通过enablelanguage(ASM)可以做到。
例如x86(32位)的MASM(Visual Studio支持的一种汇编语法):enable_language(ASM_MASM)
此外往往还需要给Visual Studio设置SEH:
汇编文件则和C/C++文件一起,正常添加为target的依赖即可:addexecutable(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种:
MultiThreaded:Compile with -MT or equivalent flag(s) to use a multi-threaded statically-linked runtime library.
MultiThreadedDLL:Compile with -MD or equivalent flag(s) to use a multi-threaded dynamically-linked runtime library.
MultiThreadedDebug:Compile with -MTd or equivalent flag(s) to use a multi-threaded statically-linked runtime library.
MultiThreadedDebugDLL:Compile with -MDd or equivalent flag(s) to use a multi-threaded dynamically-linked runtime library.
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
bash里头的做法:
powershell里头,不能用``符号,也没有cat命令。作为替代,用的是$(type options.txt),例如:
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)?
参考