worldsite / blog.sc

Blogging soul chat, stay cool. via: https://blog.sc

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CMake与VisualStudio工程配置映射

suhao opened this issue · comments

commented

本文将迁移至Rapid C++: CMake与VisualStudio工程配置映射

本文整理了工作中常用的CMake与VisualStudio工程配置的映射关系,便于基于现有VS工程迁移到CMake,以及保持开源项目与现有项目的编译兼容性。

工作中的项目工程使用VisualStudio2019进行编译,而开发机已经升至最新的VisualStudio2022,故而会包含相关版本上的兼容讨论。工程配置以VisualStudioCommunity2022Preview版本为参考。

一、CMake概念与配置

参考CMake Wiki.

1. CMakeList.txt:常规CMake配置文件,配置工程名称、生成选项等,是所有生成所必须的

PROJECT(main)
CMAKE_MINIMUM_REQUIRED(VERSION 3.15)
SET(CMAKE_SOURCE_DIR .)
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
ADD_EXECUTABLE(main ${DIR_SRCS})

2. CMAKE_BUILD_TYPE:可枚举值为Debug、Release、RelWithDebInfo、MinSizeRel

  • Debug:CMake会使用CMAKE_FLAGS_DEBUG和CMAKE_C_FLAGS_DEBUG中的字符串作为编译选项生成Makefile
  • Release:使用CMAKE_CXX_FLAGS_RELEASE和CMAKE_CFLAGS_RELEASE选项生成Makefile

3. CMAKE_EXE_LINKER_FLAGS:链接器标志

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /machine:x86")

4. add_dependencies:项目引用Reference,添加依赖

5. source_group( header FILES includeme.h ):Put files into folders

二、Visual Studio与CMake配置映射表

参考Windows C++ project property page reference.

Visual Studio Configuration Properties 常用修改配置 CMake 备注
Configuration Debug/Release/RelWithDebInfo/MinSizeRel CMAKE_BUILD_TYPE debug和release版本
General=>General Properties 通用配置
Output Directory 输出路径,$(ROOT)\pub\bin$(Configuration)
Intermediate Directory 临时文件路径,$(ROOT)\intermediate$(Configuration)$(ProjectName)
Target Name 项目名字,$(ProjectName)
Configuration Type image 项目生成类型,exe/dll/lib/utility
Windows SDK Version 10.0(latest installed version)
Platform Toolset Visual Studio 2019(v142)
C++ Language Standard Default(ISO C++ 14 Standard)
C Language Standard Default(Legacy MSVC)
Advanced=>Advanced Properties 高级
Target File Extension 目标文件扩展类型,.dll/.lib/.exe等
Extensions to Delete On Clean 清理时删除的文件扩展类型,如*.obj;.tlog;.pch;.exp;.idb;*.pdb;等
Build Log File 编译日志文件,$(IntDir)$(MSBuildProjectName).log
Preferred Build Tool Architecture 64-bit (x64) 推荐的编译工具架构,推荐使用64位以加快编译速度
Use Debug Libraries No
Enable Unity (JUMBO) Build
Copy Content to OutDir
Copy Project References to OutDir
Copy Project References's Symbols to OutDir
Copy C++ Runtime to OutDir
Use of MFC Use Standard Windows Libraries
Character Set Use Unicode Character Set 字节设置,多字节/单字节
Whole Program Optimization No Whole Program Optimization 全程序优化,目前的基础架构依赖于google开源,推荐使用无全程序优化,以保障编译
MSVC Toolset Version Default,默认无改动
Advanced=>C++/CLI Properties C++/CLI属性配置
Common Language Runtime Support
.Net Target Framework Version
Enable Managed Incremental Build
Debugging=>Debugger to launch: Local Windows Debugger Windows本地调试配置,大部分时候为默认值,仅特殊情况下设置命令行参数进行启动传参调试
Command
Command Arguments
Working Directory
Attach
Debugger Type
Environment
Merge Environment
SQL Debugging
Amp Default Accelerator
VC++ Directories=>General VC++环境路径,虽然工程中修改此处配置但是个人不推荐
Executable Directories
Include Directories 建议配置C/C++中的Additional Include Directories
External Include Directories
Reference Directories
Library Directories 建议配置Linker中的Additional Library Directories
Library WinRT Directories
Source Directories
Exclude Directories
VC++ Directories=>Public Project Content
Public Include Directories
All Header Files are Public
Public C++ Module Directories
All Modules are Public
C/C++=>General C/C++通用配置
Additional Include Directories include_directories 附加包含路径
Additional #using Directories
Additional BMI Directories
Additional Module Dependencies
Additional Header Unit Dependencies
Scan Sources for Module Dependencies
Translate Includes to Imports
Debug Information Format Program Database for Edit And Continue(/Zi) set(CMAKE_C_FLAGS_DEBUG_INIT "/Zi")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/Zi")
调试信息格式
Support Just My Code Debugging
Common Language RunTime Support set_target_properties( target PROPERTIES COMPILE_FLAGS “/clr”)
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”)
公共语言运行时
Consume Windows Runtime Extension
Suppress Startup Banner set_target_properties( target PROPERTIES COMPILE_FLAGS “/nologo” ) 取消启动版权标志和信息
Warning Level Level3(/W3) set_target_properties( target PROPERTIES COMPILE_FLAGS “/W0” )
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” )
警告级别
Treat Warnings As Errors set_target_properties( target PROPERTIES COMPILE_FLAGS “/WX-" )#No
set_target_properties( target PROPERTIES COMPILE_FLAGS “/WX" ) #Yes
将警告认为是错误,默认设置为No,个别时候设置为True
Warning Version
Diagnostics Format
SDL checks
Multi-processor Compilation set_target_properties( target PROPERTIES COMPILE_FLAGS “/MP" )#Yes
#Don’t set means No
多核心编译
Enable Address Sanitizer
C/C++=>Optimization 优化
Optimization Disabled(/Od) set(CMAKE_C_FLAGS_DEBUG_INIT "/Od")
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/O1")
set(CMAKE_C_FLAGS_RELEASE_INIT "/O2")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/O2")
警用优化
Inline Function Expansion set(CMAKE_C_FLAGS_DEBUG_INIT "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1")
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")
Enable Intrinsic Functions Yes(/Oi) set_target_properties( target PROPERTIES COMPILE_FLAGS “/Oi" ) #yes
#Don’t set means no
开启内联
Favor Size Or Speed set_target_properties( target PROPERTIES COMPILE_FLAGS “/Os" ) #size
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Ot" ) #speed
#Don’t set means neither
Omit Frame Pointers set_target_properties( target PROPERTIES COMPILE_FLAGS “/Oy-" ) #no
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Oy" ) #yes
Enable Fiber-Safe Optimizations set_target_properties( target PROPERTIES COMPILE_FLAGS “/GT" ) #yes
#not setting means no
Whole Program Optimization No set_target_properties( target PROPERTIES COMPILE_FLAGS “/GL" ) #yes
#not setting means no
C/C++=>Preprocessor 预处理
Preprocessor Definitions _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS;%(PreprocessorDefinitions) set_target_properties( target PROPERTIES COMPILE_DEFINITIONS DEFNAME=DEFVAL )
set_source_files_properties( filename.cpp PROPERTIES COMPILE_DEFINITIONS DEFNAME=DEFVAL )
预定义宏
Undefine Preprocessor Definitions set_target_properties( target PROPERTIES COMPILE_FLAGS “/UDEFNAME" )
Undefine All Preprocessor Definitions set_target_properties( target PROPERTIES COMPILE_FLAGS “/u" )
Ignore Standard Include Paths set_target_properties( target PROPERTIES COMPILE_FLAGS “/X" )
Preprocess to a File set_target_properties( target PROPERTIES COMPILE_FLAGS “/P" )
Preprocess Suppress Line Numbers set_target_properties( target PROPERTIES COMPILE_FLAGS “/EP" )
Keep Comments set_target_properties( target PROPERTIES COMPILE_FLAGS “/C" )
Use Standard Conforming Preprocessor
C/C++=>Code Generation 代码生成
Enable String Pooling set_target_properties( target PROPERTIES COMPILE_FLAGS “/GF" ) #yes
set_target_properties( target PROPERTIES COMPILE_FLAGS “/GF-" ) #no
Enable Minimal Rebuild No(/Gm-) set_target_properties( target PROPERTIES COMPILE_FLAGS “/Gm" ) #yes
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Gm-" )#no
最小化生成
Enable C++ Exceptions Yes(/EHsc) set_target_properties( target PROPERTIES COMPILE_FLAGS “/EHsc" ) #yes
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
Smaller Type Check set_target_properties( target PROPERTIES COMPILE_FLAGS “/RTCc" ) #yes
not setting means no
Basic Runtime Checks set_target_properties( target PROPERTIES COMPILE_FLAGS “/RTCs" ) #Stack frame check
set_target_properties( target PROPERTIES COMPILE_FLAGS “/RTCu" ) #Uninitialized Variable
set_target_properties( target PROPERTIES COMPILE_FLAGS “/TRC1" ) #Both
#not setting means no
Runtime Library Multi-threaded DLL(/MD) 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.
多线程Dll,同进程组件需要保持一致
Struct Member Alignment set_target_properties( target PROPERTIES COMPILE_FLAGS “/Zp1" )
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" )
Security Check set_target_properties( target PROPERTIES COMPILE_FLAGS “/GS" ) #yes
set_target_properties( target PROPERTIES COMPILE_FLAGS “/GS-" ) #no
Control Flow Guard
Enable Function-Level Linking Yes(/Gy) set_target_properties( target PROPERTIES COMPILE_FLAGS “/Gy" ) #yes
set_target_properties( target PROPERTIES COMPILE_FLAGS “/Gy-" ) #no
开启函数级别链接
Enable Parallel Code Generation
Enable Enhanced Instruction Set set_target_properties( target PROPERTIES COMPILE_FLAGS “/arch:SSE" )
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)

image

在使用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

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

4. 判断平台:32位、64位

方法1:CMAKE_SIZEOF_VOID_P 表示 void* 的大小(例如为 4 或者 8),可以使用其来判断当前构建为 32 位还是 64 位,CMake官方推荐

if(CMAKE_SIZEOF_VOID_P EQUAL 8)
    message(STATUE "64bit")
else()
    message(STATUE "32bit")endif()

方法2:判断**CMAKE_CL_64** 是否为true,CMake官方已废弃,此判断仅且仅当使用cl.exe时有效

if(CMAKE_CL_64)
    message(STATUS "MSVC 64bit")
else()
    message(STATUS "MSVC 32bit")
endif()

5. 判断Visual Studio版本:MSVC_VERSION

6. 判断操作系统:其中WIN32判断的是windows系统,包括32位和64位两种情况

if(WIN32)
    message(STATUS "----- This is Windows.")
elseif(UNIX)
    message(STATUS "----- This is UNIX.") #Linux下输出这个
elseif(APPLE)
    message(STATUS "----- This is APPLE.")
elseif(ANDROID)
    message(STATUS "----- This is ANDROID.")
endif(WIN32)

if (CMAKE_SYSTEM_NAME MATCHES "Windows")
    message(STATUS "----- OS: Windows")
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
    message(STATUS "----- OS: Linux")
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
    message(STATUS "----- OS: MacOS X")
elseif(CMAKE_SYSTEM_NAME MATCHES "Android")
    message(STATUS "----- OS: Android")endif()

测试发现,如果在CMAKE_MINIMUM_VERSION()后立即使用CMAKE_SYSTEM_NAME,Linux下得到结果为空,Android下得到为Android。看起来是Android的toolchain中进行了设定。

7. 判断Debug/Release

  • CMAKE_BUILD_TYPE取值:默认值由编译器决定,调用cmake时可通过-DCMAKE_BUILD_TYPE=Release的形式指定其值
  • Debug:CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE EQUAL "None" OR NOT CMAKE_BUILD_TYPE
  • Release:CMAKE_BUILD_TYPE MATCHES "Release"
  • RelWitchDebInfo:CMAKE_BUILD_TYPE MATCHES "RelWitchDebInfo"
  • MinSizeRel:CMAKE_BUILD_TYPE MATCHES "MinSizeRel"

8. 根据Debug/Release添加不同的库目录

set(PROJECT_LIB_DIR, "path")

在vs平台下,会自动把path和path/$(Configuration)添加到库搜索目录。

9. 设定编译选项

修改CMAKE_C_FLAGS、CMAKE_CXX_FLAGS变量

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /we4013 /we4431")

10. SAFESEH报错

error LNK2026: 模块对于 SAFESEH 映像是不安全的
fatal error LNK1281: 无法生成 SAFESEH 映像

解决办法是:

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")

11. link_directory但是链接异常

link_directories() 这句话必须在add_executable()之前写 不然找不到库目录

或者先add_executable() 再 target_link_directories(XXX PRIVATE some direcotry)

12. Debug库带“d”后缀

设置debug模式下编译出的库文件,相比于release模式下,多带一个字母"d"作为后缀。

  • 对于单个目录:set_target_properties(TargetName PROPERTIES DEBUG_POSTFIX d)
  • 对于整个CMakeLists.txt:set(CMAKE_DEBUG_POSTFIX d)
  • 在调用cmake时临时指定:-DCMAKE_DEBUG_POSTFIX=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

foreach(lib_name_pth ${LIBS_TO_COPY})
    message(STATUS "--- ${lib_name_pth}")
    execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${lib_name_pth} ${SO_OUTPUT_PATH})
endforeach()

14. 转换相对路径为绝对路径

get_filename_component(SO_OUTPUT_PATH_ABS ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${ANDROID_ABI} ABSOLUTE)

15. 循环处理列表:cmake中的列表也是字符串,不过,通过list(APPEND)得到的列表字符串,可以用foreach来遍历其中每个字符串

foreach(loop_var arg1 arg2 arg3)
    message(STATUS "--- ${loop_var}")
endforeach(loop_var)

foreach(loop_var ${SNPE_LIB_ALL})
    message(STATUS "--- ${loop_var}")
endforeach(loop_var)

16. 设置C/C++编译器

通过设定CMAKE_C_COMPILER和CMAKE_CXX_COMPILER来做到。

注意:project()命令必须在设定编译器之后出现,否则编译器的设定不起作用,将使用系统默认编译器。

if (UNIX)
    message(STATUS "----- This is Linux.")
    set(CMAKE_C_COMPILER "gcc-4.9")
    set(CMAKE_CXX_COMPILER "g++-4.9")
endif()

project(gamma)

注:前一种方法是在单个CMakeLists.txt中设定。对于跨平台编译,则应当避免污染根CMakeLists.txt,应该为每个平台分别使用cmake cache script。而在cache script中需要设定的变量,都应该是缓存变量。

set(CMAKE_C_COMPILER gcc CACHE STRING "C compiler")
set(CMAKE_CXX_COMPILER g++ CACHE STRING "C++ compiler")
set(PLATFORM_NAME "SigmaStar" CACHE STRING "")

17. 设定导入库(IMPORTED)及其属性

如果是自己项目中的源码基于cmake构建,其中利用add_library()创建的库目标,可以直接用来作为可执行目标、动态库或静态库的依赖库直接使用。

而如果是别人直接丢过来的库和头文件、没有用cmake封装一次呢?显然我们不应该在Visual Studio的项目属性中手动添加,手写一个导入库的cmake,在add_library()命令中指定关键字IMPORTED,再用set_target_properties()命令来设定导入库目标的头文件目录、库目录、库文件名字:

add_library(rock SHARED IMPORTED GLOBAL)
set_target_properties(rock PROPERTIES
    INTERFACE_INCLUDE_DIRECTORIES "inc"            #PUBLIC头文件目录
    IMPORTED_IMPLIB "rock.lib" #Windows平台上dll库的.lib库所在位置
    IMPORTED_LOCATION "rock.dll"  #dll库的.dll所在位置,或者.so库的位置,或者静态库的位置
)

其中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++

解决步骤:

  1. 用cmake-gui.exe或ccmake加载cmake的cache文件
  2. 查找需要修改的字符串对应的CMake变量
  3. 在CMakeLists.txt中修改、覆盖此变量

19. 添加宏定义

add_definitions(-DUSE_OPENCV) 
add_definitions(-DLANDMARK_VERSION=2.1.33)

相当于传递给C/C++编译器:

#define USE_OPENCV
#define LANDMARK_VERSION 2.1.33

20. 设置fPIE

error: Android 5.0 and later only support position-independent executables (-fPIE)

问题出现在:连接一个静态库到一个可执行程序,并在android6.0上运行

解决办法:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -pie")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fPIE -pie")

21. 设置fPIC

问题出现场景:编译动态库libaisf_bodyattr_processor.so的时候,它依赖于静态库libarcsoft_bsd.a,但是libarcsoft_bsd.a库编译时没有指定fPIC编译选项。

  • 在编译静态库的时候,全局设定:set(CMAKE_POSITION_INDEPENDENT_CODE ON)

  • 在编译静态库的时候,设定

add_library(lib1 lib1.cpp)
set_property(TARGET lib1 PROPERTY POSITION_INDEPENDENT_CODE ON)

22. Linux gcc添加链接库"-lm":target_link_libraries(xxx m)

23. 清空普通变量:unset()

24. 清除缓存变量:unset( CACHE)

25. FindXXX.cmake简单例子

使用场景满足的条件:

  • 使用了很多依赖项;
  • 每个依赖项仅仅提供了.a/.lib/.so库文件和.h/.hpp头文件,没有提供XXX-config.cmake脚本;
  • 每个依赖项的库文件包括debug和release两种

此时如果继续在CMakeLists.txt中“一把梭”,各种设定都写在单个文件中,可以执行就不够高了。每个依赖写成FindXXX.cmake,则后续直接使用find_package(XXX)很方便,定位排查依赖项问题、可移植性都得到了增强。

FindXXX.cmake基本步骤

  • 步骤1:FindXXX.cmake第一行:include(FindPackageHandleStandardArgs)
  • 步骤2:想方设法设定_INCLUDE_DIRS和_LIBRARIES的值,并且避免硬编码。具体又可以包括:
    • 使用set()和list(APPEND )来设定变量的值
    • 使用find_library()来找库文件和头文件(比直接写为固定值要灵活)
  • 步骤3:find_package_handle_standard_args( DEFAULT_MSG _INCLUDE_DIRS _LIBRARIES)
  • 步骤4:根据是否查找成功进行打印

例子1:单个头文件和单个库文件

# provides `milk_LIBRARIES` and `milk_INCLUDE_DIRS` variable
# usage: `find_package(milk)`

include(FindPackageHandleStandardArgs)

set(milk_ROOT_DIR ${PROJECT_SOURCE_DIR}/third_party/milk CACHE PATH "Folder contains milk") 

set(milk_DIR ${milk_ROOT_DIR})

find_path(milk_INCLUDE_DIRS
          NAMES milk.h
          PATHS ${milk_DIR}
          PATH_SUFFIXES include include/x86_64 include/x64
          DOC "milk include" 
          NO_DEFAULT_PATH)

# find milk.libfind_library(milk_LIBRARIES
             NAMES milk
             PATHS ${milk_DIR}
             PATH_SUFFIXES lib lib64 lib/x86_64 lib/x86_64-linux-gnu lib/x64 lib/x86
             DOC "milk library"
             NO_DEFAULT_PATH)

find_package_handle_standard_args(milk DEFAULT_MSG milk_INCLUDE_DIRS milk_LIBRARIES)

if (milk_FOUND)
  if (NOT milk_FIND_QUIETLY)
    message(STATUS "Found milk: ${milk_INCLUDE_DIRS}, ${milk_LIBRARIES}")
  endif ()
  mark_as_advanced(milk_ROOT_DIR milk_INCLUDE_DIRS milk_LIBRARIES)
else ()
  if (milk_FIND_REQUIRED)
    message(FATAL_ERROR "Could not find milk")
  endif ()
endif ()

例子2:同时存在Debug和Release版本的库

希望在调用find_package(xxx)之后,Visual Studio或XCode等IDE能自动切换debug和release的库。则需要为debug库的路径添加debug字段,为release库添加optimized字段。

set(LEMON_LIBRARIES
    debug "${LEMON_DIR}/lib/debug/lemon.lib"
    optimized "${LEMON_DIR}/lib/release/lemon.lib"  )

考虑到硬编码不是一个好的方案,库文件可能放在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)该缓存变量来确保查找成功。直接给出完整例子:

include(FindPackageHandleStandardArgs)

set(LEMON_ROOT_DIR ${PROJECT_SOURCE_DIR}/third_party/lemon CACHE PATH "Folder contains lemon")

set(LEMON_DIR ${CEVA_ROOT_DIR})

set(LEMON_DIR ${LEMON_ROOT_DIR})

set(LEMON_LIBRARY_COMPONENTS lemon_core lemon_extra)

foreach(lemon_component ${LEMON_LIBRARY_COMPONENTS})
  unset(LEMON_LIBRARIES_DEBUG CACHE)
  find_library(LEMON_LIBRARIES_DEBUG
            NAMES ${lemon_component}
            PATHS ${LEMON_DIR}
            PATH_SUFFIXES lib lib/debug lib/debug
            DOC "lemon library component ${lemon_component} debug"
            NO_DEFAULT_PATH)
   unset(LEMON_LIBRARIES_RELEASE CACHE)
   find_library(LEMON_LIBRARIES_RELEASE
              NAMES ${lemon_component}
              PATHS ${LEMON_DIR}
              PATH_SUFFIXES lib lib/release lib/Release
              DOC "lemon library component ${lemon_component} release"
              NO_DEFAULT_PATH)

   list(APPEND LEMON_LIBRARIES
    debug ${LEMON_LIBRARIES_DEBUG}
    optimized ${LEMON_LIBRARIES_RELEASE}
  )
endforeach()

find_package_handle_standard_args(LEMON DEFAULT_MSG LEMON_LIBRARIES)

if (LEMON_FOUND)
  if (NOT LEMON_FIND_QUIETLY)
    message(STATUS "Found LEMON: ${LEMON_LIBRARIES}")
  endif ()
  mark_as_advanced(LEMON_ROOT_DIR LEMON_LIBRARIES)
else ()
  if (LEMON_FIND_REQUIRED)
    message(FATAL_ERROR "Could not find lemon")
  endif ()
endif ()

例子3:找dll

注意:CMAKE_FIND_LIBRARY_SUFFIXES的使用:CMake find_library matching behavior?

set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")

实际上,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各种编译链接参数的默认值

cmake_minimum_required(VERSION 3.2)

message(STATUS "CMAKE_C_FLAGS = " ${CMAKE_C_FLAGS})
message(STATUS "CMAKE_C_FLAGS_DEBUG = " ${CMAKE_C_FLAGS_DEBUG})
message(STATUS "CMAKE_C_FLAGS_MINSIZEREL = " ${CMAKE_C_FLAGS_MINSIZEREL})
message(STATUS "CMAKE_C_FLAGS_RELEASE = " ${CMAKE_C_FLAGS_RELEASE})
message(STATUS "CMAKE_C_FLAGS_RELWITHDEBINFO = " ${CMAKE_C_FLAGS_RELWITHDEBINFO})

message(STATUS "CMAKE_CXX_FLAGS = " ${CMAKE_CXX_FLAGS})
message(STATUS "CMAKE_CXX_FLAGS_DEBUG = " ${CMAKE_CXX_FLAGS_DEBUG})
message(STATUS "CMAKE_CXX_FLAGS_MINSIZEREL = " ${CMAKE_CXX_FLAGS_MINSIZEREL})
message(STATUS "CMAKE_CXX_FLAGS_RELEASE = " ${CMAKE_CXX_FLAGS_RELEASE})
message(STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO = " ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})

message(STATUS "CMAKE_EXE_LINKER_FLAGS = " ${CMAKE_EXE_LINKER_FLAGS})
message(STATUS "CMAKE_EXE_LINKER_FLAGS_DEBUG = " ${CMAKE_EXE_LINKER_FLAGS_DEBUG})
message(STATUS "CMAKE_EXE_LINKER_FLAGS_MINSIZEREL = " ${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL})
message(STATUS "CMAKE_EXE_LINKER_FLAGS_RELEASE = " ${CMAKE_EXE_LINKER_FLAGS_RELEASE})
message(STATUS "CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO = " ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO})

message(STATUS "CMAKE_MODULE_LINKER_FLAGS = " ${CMAKE_MODULE_LINKER_FLAGS})
message(STATUS "CMAKE_MODULE_LINKER_FLAGS_DEBUG = " ${CMAKE_MODULE_LINKER_FLAGS_DEBUG})
message(STATUS "CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL = " ${CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL})
message(STATUS "CMAKE_MODULE_LINKER_FLAGS_RELEASE = " ${CMAKE_MODULE_LINKER_FLAGS_RELEASE})
message(STATUS "CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO = " ${CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO})

message(STATUS "CMAKE_SHARED_LINKER_FLAGS = " ${CMAKE_SHARED_LINKER_FLAGS})
message(STATUS "CMAKE_SHARED_LINKER_FLAGS_DEBUG = " ${CMAKE_SHARED_LINKER_FLAGS_DEBUG})
message(STATUS "CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL = " ${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL})
message(STATUS "CMAKE_SHARED_LINKER_FLAGS_RELEASE = " ${CMAKE_SHARED_LINKER_FLAGS_RELEASE})
message(STATUS "CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO = " ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO})

message(STATUS "CMAKE_STATIC_LINKER_FLAGS = " ${CMAKE_STATIC_LINKER_FLAGS})
message(STATUS "CMAKE_STATIC_LINKER_FLAGS_DEBUG = " ${CMAKE_STATIC_LINKER_FLAGS_DEBUG})
message(STATUS "CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL = " ${CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL})
message(STATUS "CMAKE_STATIC_LINKER_FLAGS_RELEASE = " ${CMAKE_STATIC_LINKER_FLAGS_RELEASE})
message(STATUS "CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO = " ${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO}

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中,可以设定链接器的全局设定:

if(CMAKE_SYSTEM_NAME MATCHES "Windows")
	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /WHOLEARCHIVE")
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
	 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--whole-archive")
endif()

缺点:

  • 所有库的符号都进行导入,不能灵活处理单个不需要导入所有符号的库
  • 系统默认导入的库,例如Windows下的USER32.dll和KERNEL32.dll会产生冲突

TODO: 对于单个target,如何设定?

if (CMAKE_SYSTEM_NAME MATCHES "Windows")
    set_target_properties(inter
        PROPERTIES LINK_FLAGS
        "/WHOLEARCHIVE:gender /WHOLEARCHIVE:smile"
    )
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
    # 不起作用
    set_target_properties(inter
        PROPERTIES LINK_FLAGS
        "-Wl,--whole-archive gender
   -Wl,--whole-archive smile"
    )
endif()

或者:

set(MYLIB -Wl,--whole-archive mytest -Wl,--no-whole-archive)
target_link_libraries(main ${MYLIB})

实际上:

  • 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

  • gcc和ld 中的参数 --whole-archive 和 --no-whole-archive

  • lld中的-force_load参数功能说明是在邮件列表中找到的,官方文档里还更新出来

  • ndk-20的change log

28. 生成compile_commands.json:用于在VSCode等编辑器/IDE中给C/C++代码做函数定义跳转支持

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

29. 子目录CMakeLists.txt中产生变量给父目录中的CMakeLists.txt使用

set设定变量并且设定PARENT_SCOPE参数。当项目中代码变多,就可能需要分成多个目录存放。每个目录下放一个CMakeLists.txt,写出它要处理的文件列表,然后暴露给外层CMakeLists.txt,使外层CMakeLists.txt保持清爽结构。

set(hello_srcs
    ${CMAKE_CURRENT_SOURCE_DIR}/hello.cpp)
set(hello_private_incs
    ${CMAKE_CURRENT_SOURCE_DIR}/hello.h)

set(hello_srcs ${hello_srcs} PARENT_SCOPE)
set(hello_private_incs ${hello_private_incs} PARENT_SCOPE)

30. 在IDE中将targets分组显示:使用folder

包括两步:

  • 在最顶部的CMakeLists.txt中添加一句:set_property(GLOBAL PROPERTY USE_FOLDERS ON)
  • 在希望出现在文件夹的项目的add_library或add_executable后添加:set_property(TARGET ${prj_target} PROPERTY FOLDER Samples)

image

31. 设置Debug的优化级别参数

set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")

32. cmake生成VS2019的工程

VS2019开始,需要用-A参数指定是32位程序,还是64位程序。以前的用法不行了

cmake -G "Visual Studio 16 2019" -A Win32 ..\..
cmake -G "Visual Studio 16 2019" -A x64 ..\..

33. 不要同时使用include_directories()和target_include_directories()

include_directories("inc" "src")

add_library(rock src/rock.cpp)

target_include_directories(rock PRIVATE "3rdparty/spdlog")

用法有坑,其中target_include_directories()设置的目录不会生效。经验:只使用其中一种设定include目录的方式。

34. NDK开发中出现error: cannot use 'try' with exceptions disabled

这需要编译器的编译选项中开启exception的支持。现在NDK开发,主流的做法是Android Studio + gradle + cmake做构建,需要在build.gradle中设定-fexceptions:

        externalNativeBuild { 
           cmake 
{//                cppFlags '-std=c++11 -fexceptions'
//                arguments '-DANDROID_PLATFORM=android-21',
//                    '-DANDROID_TOOLCHAIN=clang',
//                    '-DCMAKE_BUILD_TYPE="Release',
//                    '-DANDROID_ARM_NEON=ON',
//                    '-DANDROID_STL=c++_shared'
//                cppFlags '-std=c++11'
//                arguments '-DANDROID_TOOLCHAIN=clang',
//                        '-DANDROID_STL=c++_static'
                cppFlags "-std=c++11 -fexceptions"
            }
        }

但有时候,发现上述设定后并不生效;尝试删除.externalBuild目录重新构建,仍然报exception无法处理。后来发现,问题出在CMakeLists.txt加载的.cmake脚本中(用的代码框架是其它部门同事写的,所以不是很熟悉),他给手动设定了这么一句:

SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions -fno-short-enums -Werror=non-virtual-dtor")

去掉-fno-exception即可。

35. cmake-gui中可见(可检索)的变量

目前遇到的有两种:

  • option( )命令定义的变量:option(USE_OPENCV “use opencv?” CACHE PAH)
  • 缓存变量:set(varName "value" CACHE STRING "")

当然,还可以使用mark_as_adances来设定,则默认在cmake-gui中不可见

36. Ninja error: Filename longer than 260 characters

NDK开发中,CMake+Ninja构建,如果文件名超过260个字符会失败。这个限制略蛋疼

ninja: error: Stat(../../deps/arcsoft_landmark/2.1.12021.33/libs/Android/armeabi-v7a/libarc_net_sgl.a;E:/dbg/myy/landmark_demo/deps/arcsoft_landmark/2.1.12021.33/libs/Android/armeabi-v7a/libarcsoft_face_detection.a;E:/dbg/myy/landmark_demo/deps/arcsoft_landmark/2.1.12021.33/libs/Android/armeabi-v7a/libarcsoft_landmark_tracking.a;E:/dbg/myy/landmark_demo/deps/arcsoft_landmark/2.1.12021.33/libs/Android/armeabi-v7a/libarcsoft_videooutline.a): Filename longer than 260 characters

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。

常见的平台下对应输出:

  • MSVC
  • Clang
  • GNU
  • AppleClang

更多结果看官方文档 CMAKE__COMPILER_ID

38. cmake字符串追加元素,并且用空格分隔

简单有效的两种方式:

  • set: set(MY_FLAGS "${MY_FLAGS} -Werror=shadow")
  • 封装一个函数
function(afq_list_append __string __element)
    set(${__string} "${${__string}} ${__element}" PARENT_SCOPE)
endfunction()

39. cmake --build的使用:cmake执行编译链接、安装

本质上,当使用cmake ..,或cmake -G "Visual Studio 15 2017 Win64" ../..类似命令时,是执行“pre-make”,相当于是makefile的生成器,可以说对于你的项目代码来说并没执行编译链接,更没有安装。

实际使用经验:cmake生成了这些cache文件后,可以打开Visual Studio编译,或执行make编译(Linux下)。但这些都是native tool。通用的方式则是用cmake包装好的接口:

# 执行编译(如果是可执target,则包括链接过程)
cmake --build . --config Release

# 执行某个target的编译(如果是可执target,则包括链接过程)
cmake --build . --config Release --target xx

# 执行安装
cmake --install . --prefix d:/lib/openblas/clang-cl/x64 -v

40. C/C++和汇编混编

在cmake里混合编译C/C++与汇编代码,通过enable_language(ASM_)可以做到。

例如x86(32位)的MASM(Visual Studio支持的一种汇编语法):enable_language(ASM_MASM)

此外往往还需要给Visual Studio设置SEH:

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")

汇编文件则和C/C++文件一起,正常添加为target的依赖即可:add_executable(run src/main.cpp src/CalcSum_.asm)

41. cmake设置pthread

  • target_link_libraries(xxx pthread):有时候不管用(例如ARM Android平台),则用下面的方式
  • find_package( Threads ) && target_link_libraries( testbed ${CMAKE_THREAD_LIBS_INIT} log)
  • 注意ARM Android(也就是通常说的NDK开发中),需要给cmake传一个选项:-DANDROID_PLATFORM=android-24 ^(我是armv8所以用24)

42. cmake使用pkg-config

有些依赖库只提供.pc文件,甚至已经配置了CMake脚本但是安装后还是只有.pc而没有XXXConfig.cmake或xxx-config.cmake。并且不仅是Linux,Windows上也这样。
这就不得不在CMake中尝试去加载.pc文件。原理是,cmake里面封装了对pkg-config工具的兼容,可以认为是一个插件,用这个插件去加载.pc文件。实际测试发现Linux和Windows都可以用。

  • Linux安装pkg-config:sudo apt install pkg-config
  • Windows安装pkg-config-lite

使用:先找到xx.pc文件,然后分成目录和文件前缀两部分,在cmake中配置

举例1:Ubuntu 16.04下用apt安装openblas并在CMake中用pkg-config方式配置:

cmake_minimum_required(VERSION 3.15)

project(cmake_pkg_config_example)

set(ENV{PKG_CONFIG_PATH} /usr/lib/pkgconfig)
find_package(PkgConfig)
pkg_search_module(OBS REQUIRED blas-openblas)

message(STATUS "=== OBS_LIBRARIES: ${OBS_LIBRARIES}")
message(STATUS "=== OBS_INCLUDE_DIRS: ${OBS_INCLUDE_DIRS}")

举例2:Windows 10下用CMake配置Pangolin安装中配置的zlib

cmake_minimum_required(VERSION 3.15)

project(cmake_pkg_config_example) 

#指定pkg-config.exe绝对路径
set(PKG_CONFIG_EXECUTABLE "D:/soft/pkg-config/bin/pkg-config.exe") 

#指定zlib.pc所在目录
set(ENV{PKG_CONFIG_PATH} "D:/lib/pangolin/share/pkgconfig") 

find_package(PkgConfig)
message(STATUS "--- PKG_CONFIG_FOUND: ${PKG_CONFIG_FOUND}")
message(STATUS "--- PKG_CONFIG_VERSION_STRING: ${PKG_CONFIG_VERSION_STRING}")

pkg_search_module(ZLIB REQUIRED zlib) 

message(STATUS "=== ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}")
message(STATUS "=== ZLIB_INCLUDE_DIRS: ${ZLIB_INCLUDE_DIRS}")

43. cmake多行注释

#[[
第一种注释方式
#]]


#[===============[
第二种注释方式
#]===============]

44. 命令行-D指定参数

在cmake脚本中指定其中任意一种:

  • cmake缓存类型的变量:set(CAFFE_TARGET_VERSION "1.0.0" CACHE STRING "Caffe logical version")
  • option:option(USE_OPENCV "Do we use OpenCV?" ON)

45. include()指令

  • 包含文件,例如include(utils.cmake)
  • 包含模块,在CMAKE_MODULE_PATH->CMAKE MODULE DIRECTORY依次查找。例如include(ExternalModule),会在这两个路径列表中查找ExternalModule.cmake文件并加载

CMAKE_MODULE_PATH是在CMake脚本中用户可以自行修改的变量。
CMAKE MODULE DIRECTORY是什么,官方文档没明确说。其实说的应该是cmake安装后的Modules目录,例如/usr/local/share/cmake/Modules

46. list追加元素,或list首部插入元素

  • 追加(链表尾部插入):list(APPEND CMAKE_MODULE_PATH "cmake/Modules")
  • 首部插入(链表首元素前插入):list(INSERT CMAKE_PREFIX_PATH 0 "$ENV{HOME}/soft/opencv/build")

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找不到。

# 我的错误设定:
set(CMAKE_C_FLAGS "${MY_CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${MY_CMAKE_CXX_FLAGS}")
# 正确做法应该是追加内容而非修改:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MY_CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MY_CMAKE_CXX_FLAGS}")

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)则可以导出所有的符号。

CMake linking against shared library on windows: error about not finding .lib file

而如果只想导出一部分符号,则可以为每个函数分别指定导出规则。

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的写法:

# 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})

https://stackoverflow.com/questions/48891928/cmake-unzip-automaticallywhile-building-when-the-zip-file-changes

53. 列出所有target

有些基于cmake的项目,CMakeLists.txt写的很复杂很庞大。可以列出所有target,帮助理清思路。

列出makefile中的所有target:

cd build
cmake --build . --target help 

实际上,makefile里诸如"all"和"clean"这样的target,并不是我们感兴趣的。还是shell大法拼凑一下吧:

cd ~/work/my_project
mkdir build && cd build && cmake ..
make -j4 > log.txt 2>&1
grep 'Built target' log.txt | awk '{print $4}'

54. 判断文件是否存在

EXISTS可以判断,同时适用于文件和目录。

举例:查找当前目录下是否存在doctest目录,并且检查doctest目录下是否存在doctest_fwd.h和doctest.cpp文件:

if (EXISTS "${CMAKE_SOURCE_DIR}/doctest"
    AND EXISTS "${CMAKE_SOURCE_DIR}/doctest/doctest_fwd.h"
    AND EXISTS "${CMAKE_SOURCE_DIR}/doctest/doctest.cpp")
    message(STATUS "--- doctest source code ready !")
else()
    message(STATUS "--- extracting doctest source code from zip...")
    execute_process(
        COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/doctest.zip
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    )
endif()

55. 打印变量

message(STATUS "--- CMAKE_CXX_COMPILER is: ${CMAKE_CXX_COMPILER}")

include(CMakePrintHelpers)
cmake_print_variables(CMAKE_CXX_COMPILER)

56. 判断是否为目录

用IS_DIRECTORY命令判断。例如shadow中判断各个backend,如果是目录,则添加subdirectory,写法如下:

foreach (backend_dir ${backends_dir})
  if (IS_DIRECTORY ${backend_dir})
    add_subdirectory(${backend_dir}) 
 endif()
endforeach()

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)

  • 针对单个目标设定:

add_executable(foo foo.c)
set_property(TARGET foo PROPERTY
             MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

候选值有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)。

其次是设定如下几个变量中的其中一个(多个也行但没必要,看你需求):

  • CMAKE_EXE_LINKER_FLAGS
  • CMAKE_EXE_LINKER_FLAGS_DEBUG
  • CMAKE_SHARED_LINKER_FLAGS
  • CMAKE_SHARED_LINKER_FLAGS_DEBUG

例如我编译的是可执行目标,那么:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")

对于可执行目标,并且依赖于静态库或动态库,懒人用法:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")

注意:ASAN似乎对vector等容器的支持不够好。对于vector,预先分配多少内存,似乎ASAN并不知道,导致vector被clear后再使用,(做下标访问一段时间后)出现的segfault,没被ASAN检测到。

59. Linux下编译32位程序

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")

60. 设置VS的可执行文件生成目录

假设.sln目录在 build/vs2019-x64 下,则默认的可执行文件生成目录是 build/vs2019-x64/Debug 或 build/vs2019-x64/Release;而如果文件放在这一默认生成目录下的话无法被读取到(不加前缀的情况),需要放在 build/vs2019-x64 目录下才能读到。

以至于,代码里经常要根据 _MSC_VER 或者 ANDROID 等平台相关的宏,设定不同的路径,例如:

#if _MSC_VER
    const char* image_path = "E:/share/to_zcx/ncnn_vk_dbg/build/vs2019-x64/ncnn_input_cpu.bmp";
#elif ANDROID
   const char* image_path = "ncnn_input_cpu.bmp";
#endif

这让代码不整洁,也增加了出错的可能。

image
image

可以通过上面两截图中的方式,在项目属性中修改可执行文件的生成目录和启动目录,但仍然不方便;最好的办法还是在 CMakeLists.txt 里设定:

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_SOURCE_DIR}/bin>) 

add_executable(testbed hello.cpp)
set_target_properties(testbed PROPERTIES
    VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")

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等函数的打印过程

set(CMAKE_FIND_DEBUG_MODE TRUE)
find_package(...)
set(CMAKE_FIND_DEBUG_MODE FALSE)

实际上据网友反馈,稍早的版本也可以用这一变量,只不过文档里当时没写。

另外,设定CMAKE_FIND_DEBUG_MODE变量为TRUE,等价于调用cmake时候指定--debug-find参数。

如果你是cmake-GUI方式构建,菜单栏也可以选择输出debug信息:

image

62. 命令行方式传入option的坑

场景:命令行方式调用cmake,指定了很多option和cache variable的值,希望把这些option和cache variable放在.txt文件中,然后通过cat option.txt方式传给cmake。
结论:option.txt里的写法,-D之后不能有空格,否则无法生效。

例如,正确写法是:

-DUSE_X1=ON
-DUSE_X2=ON
-DUSE_X3=ON

错误写法是

-D USE_X1=ON
-D USE_X2=ON
-D USE_X3=ON

可以用如下 CMakeLists.txt 验证结论:

cmake_minimum_required(VERSION 3.15)

project(x)

option(USE_X1 "USE X1?" OFF)
option(USE_X2 "USE_X2?" OFF)
option(USE_X3 "USE_X3?" OFF)

if (USE_X1)
    message(STATUS "USE_X1: ON")
else()
    message(STATUS "USE_X1: OFF")
endif()

if (USE_X2)
    message(STATUS "USE_X2: ON")
else()
    message(STATUS "USE_X2: OFF")
endif()

if (USE_X3)
    message(STATUS "USE_X3: ON")
else()
    message(STATUS "USE_X3: OFF")
endif()

63. PowerShell中从txt读内容传给cmake

  • bash里头的做法:
cmake `cat options.txt`
  • powershell里头,不能用``符号,也没有cat命令。作为替代,用的是$(type options.txt),例如:
cmake G:/dev/opencv-build/mock -G "Visual Studio 16 2019" -A x64 -DCMAKE_INSTALL_PREFIX=install $(type G:/dev/opencv-build/mock/options.txt)

powershell是基于ksh语法修改而来(而不是M的别的其他的语法);‘(cat options.txt)`在bash里也能用;
两个``符号,英文正式名字叫做backtick,而不是"reverse quote"。

99. 现代CMake

常用主要有如下几个不响应target的全局设定:

参考