worldsite / blog.sc

Blogging soul chat, stay cool. via: https://blog.sc
3 stars 0 forks source link

CMake与VisualStudio工程配置映射 #40

Open suhao opened 2 years ago

suhao commented 2 years ago

本文将迁移至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

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_VOIDP 表示 void* 的大小(例如为 4 或者 8),可以使用其来判断当前构建为 32 位还是 64 位,CMake官方推荐

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

方法2:判断_CMAKE_CL64 是否为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

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"作为后缀。

13. 在cmake中执行目录创建、拷贝文件等脚本:add_custom_command、execute_process

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编译选项。

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:单个头文件和单个库文件

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

缺点:

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

实际上:

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

包括两步:

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中可见(可检索)的变量

目前遇到的有两种:

当然,还可以使用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。

常见的平台下对应输出:

更多结果看官方文档 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包装好的接口:

# 执行编译(如果是可执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++与汇编代码,通过enablelanguage(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的依赖即可: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方式配置:

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脚本中指定其中任意一种:

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

# 我的错误设定:
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设定。

58. 设定Address Sanitizer(ASAN)

首先确保有符号信息(例如CMAKE_BUILD_TYPE设定为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

99. 现代CMake

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

参考