Windows 项目的 CMakeLists 编写
前言:
项目一直是以 .sln 解决方案打开和处理的,上传到 github 也是需要将 sln 文件包括到项目里,不太优雅(虽然方便),毕竟现在开源项目基本都是使用 CMake 做跨平台编译
因为项目是以 Windows 编译为主,就只写了 CMakeLists 中的 Windows 的部分,后续如果要跨平台的话,可以在此基础上拓展。
写这篇文章的主要目的是积累和分享学习经验,在寻找相关 vs 参数设置上花费了不少时间,写下来后可以让有类似需求的同学少走些弯路
正篇:
我也是最近才接触到 CMakeLists 的写法,从一开始的静态库和动态库的简单编写到现在整个项目都使用 CMake 编写,也算是一个循序渐进的过程
静态库和动态库的编写参考我过去的文章:
好了,开始吧
我们需要先设置 CMake 的最低版本,什么是最低版本,也就是说你添加 CMake 的函数时,有些函数是在特定的 CMake 版本后才被添加进来的,如果在该版本之前添加这些函数的话,CMake 会提示说找不到这些函数并报错
比如说,source_group 函数用于项目中组织源文件,将它们分组显示在 IDE 中,使项目结构更清晰,它是 CMake 2.8.11 中新添加进来的,所以就需要设置 CMake 的最低版本为 2.8.11
cmake_minimum_required(VERSION 2.8.11)
一般情况下,我们会设置到 3.x 以后,这是通用的做法,因为有些函数会随着版本更新而添加新的参数或者功能
设置好最低 CMake 版本后,我们可能会需要设置 SDK 的最低版本,这里指的是 Windows 的 SDK,也是类似 CMake 的最低版本,有些你用的 win32 函数可能是新 SDK 添加进来的
# Check for Win SDK version 10.0.19041 or above
if(MSVC AND MSVC_VERSION LESS 1920)
message(STATUS "Windows API version is ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
string(REPLACE "." ";" WINAPI_VER "${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") list(GET WINAPI_VER 0 WINAPI_VER_MAJOR)
list(GET WINAPI_VER 1 WINAPI_VER_MINOR)
list(GET WINAPI_VER 2 WINAPI_VER_BUILD) set(WINAPI_COMPATIBLE FALSE)
if(WINAPI_VER_MAJOR EQUAL 10)
if(WINAPI_VER_MINOR EQUAL 0)
if(WINAPI_VER_BUILD GREATER_EQUAL 19041)
set(WINAPI_COMPATIBLE TRUE)
endif()
else()
set(WINAPI_COMPATIBLE TRUE)
endif()
elseif(WINAPI_VER_MAJOR GREATER 10)
set(WINAPI_COMPATIBLE TRUE)
endif() if(NOT WINAPI_COMPATIBLE)
message(FATAL_ERROR "missevan-fm requires Windows 10 SDK version 10.0.19041.0 and above to compile.\nPlease download the most recent Windows 10 SDK in order to compile (or update to Visual Studio 2019).")
endif()
endif()
设置好 Windows SDK 最低版本后,还需要添加一些库的路径,比如我们可能会使用到 Qt 库,那么我们也可以继续添加 Qt 的路径
set(CMAKE_PREFIX_PATH ${QTDIR}) if(QTDIR OR DEFINED ENV{QTDIR} OR DEFINED ENV{QTDIR32})
# Qt path set by user or env var
message(STATUS "Qt path is ${QTDIR}")
else()
set(QTDIR "D:/Qt/5.15.x/msvc2019_x86_static")
message(WARNING "QTDIR variable is missing. Please set this variable to specify path to Qt (e.g. D:/Qt/5.15.x/msvc2019_x86_static)")
endif()
CMAKE_PREFIX_PATH
是 CMake 中一个用于指定查找第三方库、头文件、CMake 模块等路径的变量。它主要用于帮助 CMake 在特定路径下查找依赖项。
也就是说我们在 Windows 的环境变量中添加了 Qt 的路径,那么使用 CMAKE_PREFIX_PATH 可以直接找到 Qt 的路径并赋值给 QTDIR
如何找不到 Qt 的路径,那么我们就手动设置 Qt 的路径
说明一下,msvc2019_x86_static 是你编译 Qt 静态库的文件夹(用于商业要付费,建议链接到动态库),如何编译 Qt 的静态库,可以参考我之前的一篇文章,
接下来要设置 VS 的各个配置
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
set(CMAKE_GENERATOR_PLATFORM Win32)
set(CMAKE_GENERATOR_TOOLSET "host=x86" CACHE STRING "Platform Toolset" FORCE)
set(CMAKE_MFC_FLAG 1)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd /Zi")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /Zi")
Debug 和 Release 下生成 pdb 文件
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /DEBUG /OPT:REF /OPT:ICF")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF")
VS 配置这块的 CMake 的函数使用合起来如下
message(STATUS "Set visual studio build params")
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
set(CMAKE_GENERATOR_PLATFORM Win32)
set(CMAKE_GENERATOR_TOOLSET "host=x86" CACHE STRING "Platform Toolset" FORCE)
set(CMAKE_SUPPRESS_REGENERATION true)
set(CMAKE_MFC_FLAG 1)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd /Zi")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /Zi")
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /DEBUG /OPT:REF /OPT:ICF")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF")
add_definitions(-D_UNICODE)
其中还有三个函数没说明,
1. message 相当于 printf 函数,用于输出字符串,这边可以在 cmake 过程中观察执行到哪一步,方便定位错误
2. CMAKE_SUPPRESS_REGENERATION
是 CMake 中的一个变量,用于指定在生成项目文件(如 Visual Studio 的 .sln 和 .vcproj 文件)时是否抑制重新生成的行为。这个变量主要用于生成项目文件时的一些配置。
设置这个变量为 TRUE
时,可以告诉 CMake 在生成项目文件时避免强制重新生成已有的项目文件。这样做可以节省一些时间,特别是在一些场景下,比如在多次配置和生成过程中,确保不会因为每次重新生成项目文件而造成不必要的延迟。
3. add_definitions(-D_UNICODE)
是 CMake 中用于向编译器添加预定义宏的命令。在这里,-D
选项表示定义一个宏,_UNICODE
是一个预定义的宏,用于指示编译器使用 Unicode 字符集。
要注意的是,CMAKE_GENERATOR_PLATFORM 和 CMAKE_GENERATOR_TOOLSET 必须要在 Project 之前使用,否则不生效,使用 clang 编译的时候,就要加上条件语句用于区分
接着定义项目名
project(my-project)
这个最直接的作用就是 cmake 生成 my-project.sln 文件
设置 C++ 标准库版本
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 14)
指定 C++ 标准为 C++14。这告诉 CMake 使用 C++14 标准来编译项目中的 C++ 代码。set(CMAKE_CXX_STANDARD_REQUIRED ON)
表示要求编译器严格遵循所指定的 C++ 标准。如果编译器不支持所设定的 C++ 标准,编译将会失败。这有助于确保项目代码只使用所选定的标准语言特性。set(CMAKE_CXX_EXTENSIONS OFF)
告诉 CMake 不要启用编译器的扩展特性。这可以确保编译器不会启用除 C++ 标准所定义之外的额外扩展功能。
之前配置完 Qt 的路径后,这时就要设置 moc 了,moc 其实就是 Qt 的反射机制,用于信号槽的连接
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
设置版本号,版本号也就是 VS 中 rc 文件里的各个值
你需要使用 configure_file 函数将 .in 文件配置成 rc 文件
configure_file
是 CMake 中的一个命令,用于在构建过程中通过读取一个文件的内容,替换其中的变量,然后生成一个新的文件。
它的主要作用是处理项目中的模板文件,将其中的变量替换为相应的值,生成一个目标文件。这个命令通常用于生成配置文件,包含了项目编译时所需的特定信息,例如版本号、路径等。
其中 MP_VERSION_MAJOR 是变量,也可以理解为宏
set(MP_VERSION 1.0.0) string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" MP_VERSION_MATCH "${MP_VERSION}") set(MP_VERSION_MAJOR ${CMAKE_MATCH_1})
set(MP_VERSION_MINOR ${CMAKE_MATCH_2})
set(MP_VERSION_REVISION ${CMAKE_MATCH_3})
# MP_BUILD_NUMBER 我们一般不设置
set(MP_BUILD_NUMBER "0")
set(MP_FILE_DESCRIPTION "XXX")
set(MP_INTERNAL_NAME "my-project.dll")
set(MP_LEGAL_COPYRIGHT "Copyright (C) xxx xxxx, Co., Ltd.")
set(MP_ORIGINAL_FILENAME "my-project.dll")
set(MP_PRODUCT_NAME "XXXXXXX")
set(MP_PRODUCT_VERSION "${MP_VERSION_MAJOR}.${MP_VERSION_MINOR}.${MP_VERSION_REVISION}.${MP_BUILD_NUMBER}")
set(MP_COMPANY_NAME "XXXXXXX有限公司")
比如说下面是我的 in 文件
// Microsoft Visual C++ generated resource script.
//
#include "resource.h" #define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h" /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS /////////////////////////////////////////////////////////////////////////////
// Chinese (Simplified, PRC) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
// 1 TEXTINCLUDE
BEGIN
"resource.h\0"
END 2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END 3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// Icon
// // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_MAIN ICON "..\\img\\my_project.ico" /////////////////////////////////////////////////////////////////////////////
//
// Version
// VS_VERSION_INFO VERSIONINFO
FILEVERSION ${MP_VERSION_MAJOR},${MP_VERSION_MINOR},${MP_VERSION_REVISION},${MP_BUILD_NUMBER}
PRODUCTVERSION ${MP_VERSION_MAJOR},${MP_VERSION_MINOR},${MP_VERSION_REVISION},${MP_BUILD_NUMBER}
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080404b0"
BEGIN
VALUE "CompanyName", "${MP_COMPANY_NAME}"
VALUE "FileDescription", "${MP_FILE_DESCRIPTION}"
VALUE "FileVersion", "${MP_PRODUCT_VERSION}"
VALUE "InternalName", "${MP_INTERNAL_NAME}"
VALUE "LegalCopyright", "${MP_LEGAL_COPYRIGHT}"
VALUE "OriginalFilename", "${MP_ORIGINAL_FILENAME}"
VALUE "ProductName", "${MP_PRODUCT_NAME}"
VALUE "ProductVersion", "${MP_PRODUCT_VERSION}"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x804, 1200
END
END #endif // Chinese (Simplified, PRC) resources
///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
// /////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
在此基础上,调用 configure_file
configure_file(missevan-fm-kernel.rc.in ${CMAKE_CURRENT_SOURCE_DIR}/build/my_project.rc)
就可以转换为需要的 my_project.rc 文件了,这样做的好处就是在 CMake 中设置 rc 中的变量,特别是每次发版都要修改的版本号
同理,对于要修改版本号这种需求,都可以使用 in 文件转换来实现
这步配置完后,我们开始进入库的配置和代码文件的配置
使用 find_package 将 Qt 库加入到项目工程中,仅添加用到的 Qt 库,其他官方库亦是如此
find_package(Qt5Core)
find_package(Qt5Gui)
find_package(Qt5Widgets)
find_package(Qt5WinExtras)
设置源码文件路径,一般我们都会将 .cpp .h 都放到 src 文件夹下,以便查找
这里我们的源码都是在 src 文件夹下,之所以定义 SRC_PATH,是下面添加每个 cpp 文件都要添加路径,这样节省了时间以及美观
set(SRC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src)
设置完 SRC_PATH 之后就可以设置各个源码子目录了,比如我们的代码层级是这样设置的
我们希望 CMake 生成的 .sln 工程在被打开也是呈现这样的目录,那么我们就需要使用 source_group 函数
先定义好 cpp 路径,我们需要手动将每个头文件和源文件都添加进来,有些人是使用比较快速的方法,就是只要后缀是 .h 和 .cpp 的文件都一键添加进来,这样做,有好有弊
我个人觉得不太好,因为实际过程中,有些 cpp 文件是过旧的,你不想删除但也不想添加到工程中,这时你如果一键添加,就不是预期的结果了
set(AUDIO_SOURCES
${SRC_PATH}/audio/1.cpp
${SRC_PATH}/audio/2.cpp
${SRC_PATH}/audio/3.cpp
) set(AUDIO_HEADERS
${SRC_PATH}/audio/1.h
${SRC_PATH}/audio/2.h
${SRC_PATH}/audio/3.h
)
source_group("Source Files/audio" FILES ${AUDIO_SOURCES})
source_group("Header Files/audio" FILES ${AUDIO_HEADERS})
source_group("Source Files/base" FILES ${BASE_SOURCES})
source_group("Header Files/base" FILES ${BASE_HEADERS})
source_group("Source Files/live" FILES ${LIVE_SOURCES})
source_group("Header Files/live" FILES ${LIVE_HEADERS})
source_group("Source Files/net" FILES ${NET_SOURCES})
source_group("Header Files/net" FILES ${NET_HEADERS})
source_group("Source Files/ui" FILES ${UI_SOURCES})
source_group("Header Files/ui" FILES ${UI_HEADERS})
这样我们的源文件和头文件就设置好了
现在配置三方库的宏路径,一般情况下我们习惯将用到的三方库都放到一个文件夹,比如 third_party
set(ADir ${CMAKE_CURRENT_SOURCE_DIR}/third_party/a)
set(BDir ${CMAKE_CURRENT_SOURCE_DIR}/third_party/b)
set(CDir ${CMAKE_CURRENT_SOURCE_DIR}/third_party/c)
接着设置预处理宏和预编译静态库
set(MY_PROJECT_PLATFORM_DEPS
aaa.lib
bbb.lib
ccc.lib
) set(COMPILE_DEFINITIONS
UNICODE
_UNICODE
WIN32
_NO_GDIPLUS_
_CRT_RAND_S
QT_CORE_LIB
QT_GUI_LIB
QT_WIDGETS_LIB
QT_WINEXTRAS_LIB
MP_VERSION="${MP_VERSION}"
)
注意预处理宏有个 MP_VERSION="${MP_VERSION}",这个实际上是将版本号由 CMakeLists 透传到项目中,这样我们就可以在项目中直接使用 MP_VERSION
const char kAppVersion[] = MP_VERSION;
项目中我们还有两个自编译的三方库,需要使用 add_subdirectory 添加进来,这样做的目的有个好处就是,每次 CMake -B build 时,都会一同将子项目的 CMakeLists.txt 一同编译
使用 msbuild 编译 .sln 工程除了编译主项目也会将添加进来的子项目一同编译出来
注意:每个子项目也需要一个 CMakeLists.txt
我们的自编译的三方库都是编译成静态库
add_subdirectory(${ADir}/A/)
add_subdirectory(${BDir}/B/)
如何编译成静态库,可以参考我的另一篇文章:
子项目的 CMakeLists.txt 位置就在 ${ADir}/A/ 下面,像这样,${ADir}/A/CMakeLists.txt,${BDir}/B/CMakeLists.txt
设置完这些,使用 add_executable 添加可执行文件
add_executable(my_project
${AUDIO_SOURCES} ${AUDIO_HEADERS}
${AUDIO_CAPTURES_SOURCES} ${AUDIO_CAPTURES_HEADERS}
${BASE_SOURCES} ${BASE_HEADERS}
${LIVE_SOURCES} ${LIVE_HEADERS}
${NET_SOURCES} ${NET_HEADERS}
${RESOURCE_FILES}
)
下一步我们开始设置工程的属性
工程中如果有自定义宏文件,如下,
对于这种文件,我们需要找到文件的位置,并使用 set_target_properties 添加到工程中
set_target_properties(my_project PROPERTIES VS_USER_PROPS "${CMAKE_SOURCE_DIR}/projects/third_party.props")
其他的一些工程的属性设置也是类似
# enable vcpkg
set_target_properties(missevan_fm_kernel PROPERTIES VS_GLOBAL_VcpkgEnabled true)
# Release 下生成 pdb 文件
set_target_properties(missevan_fm_kernel PROPERTIES LINK_FLAGS "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF")
需要注意的是,VS_GLOBAL_VcpkgEnabled 只能设置 Use Vcpkg 选项
如果你要设置下面的 Triplet 和 Vcpkg Configuration 选项,目前没有办法做到,我是通过 powershell 脚本强行修改 vcxproj 文件做到修改 Triplet 的选项
powershell 脚本的编写,我后续会在另一篇文章中介绍
如果你还需要设置 .def 文件,可以使用 target_link_options 做到,def 文件你可以将它视为 dllexport 的等同效果,一般用来暴露工程的 main 函数
if (CMAKE_BUILD_TYPE STREQUAL "Release")
target_link_options(my_project PRIVATE /DEF:${CMAKE_CURRENT_SOURCE_DIR}/projects/my_project.def)
endif ()
接下来是添加头文件路径,使用 include_directories 或者 target_include_directories,target_include_directories 是比较现代的做法,官方是推荐使用后者的
不过我是用的前者,理由是使用 target_include_directories 时,总会给我添加一些稀奇古怪的路径
include_directories(
${SRC_PATH}/
${SRC_PATH}/my_project/
${FaacDir}/include/
${ADir}/include/
${BDir}/include/
${CDir}/include/
${QTDIR}/include/
${QTDIR}/include/QtCore/
${QTDIR}/include/QtGui/
${QTDIR}/include/QtANGLE/
${QTDIR}/include/QtWidgets/
${QTDIR}/include/QtWinExtras/
)
添加库路径,使用 target_link_directories
target_link_directories(missevan_fm_kernel PRIVATE
${ADir}/lib/
${BDir}/lib/
${CDir}/lib/
${CMAKE_CURRENT_SOURCE_DIR}/build/DDir/$<CONFIG>/
${QTDIR}/lib/
${QTDIR}/plugins/imageformats/
${QTDIR}/plugins/platforms/
${QTDIR}/plugins/audio/
${QTDIR}/plugins/styles/
)
${CMAKE_CURRENT_SOURCE_DIR}/build/DDir/$<CONFIG>/ 这行说明一下
当我们使用 add_subdirectory 添加三分库时,因为分为 Debug 和 Release 调试模式,故指定的库路径也有所不同,比如 Debug 的库在 Debug 文件夹下,Release 的库在 Release 文件夹下
$<CONFIG> 会在两种模式下自动赋值为对应的调试模式,比如现在用的是 Debug 模式,那 $<CONFIG> 也会变成 Debug,不过对于 DebugDll 和 ReleaseDll,$<CONFIG> 无法转换。
还有,target_link_directories 在 Windows 添加的路径很是奇葩
它会在每行路径下新添加一个 xxx/$(Configuration) 路径,搜了一下类似的案例,发现很多人都遇到过,是个顽疾
官方虽然开了 case 尝试修复,目前为止,仍然处于“怠工”阶段
接着添加三方依赖库,也就是 Link/Input->Additional Dependencies,使用 target_link_libraries
target_link_libraries(my_project
PRIVATE
A
B
$<$<CONFIG:Debug>:
libcmtd.lib
Qt5Cored.lib
Qt5Guid.lib
Qt5Widgetsd.lib
Qt5WinExtrasd.lib
>
$<$<CONFIG:Release>:
Ole32.lib
Shell32.lib
Shlwapi.lib
User32.lib
Gdi32.lib
Qt5Core.lib
Qt5Gui.lib
Qt5Widgets.lib
Qt5WinExtras.lib
>
)
其中 A 和 B 是使用 add_subdirectory 添加的子目录,这样做的好处是它会自动添加子库的依赖
接着添加预编译宏,使用 target_compile_definitions
target_compile_definitions(my_project
PRIVATE
$<$<CONFIG:Debug>:
QT_QML_DEBUG
_DEBUG
_CONSOLE
>
$<$<CONFIG:Release>:
QT_NO_DEBUG
NDEBUG
>
)
接着设置链接器子系统,也就是 Windows 和 Console,一般 Debug 下使用 Console,因为需要弹出控制台查看调试信息,Release 下则是 Windows
注意 Console 对应的是 main 入口,Windows 对应的是 wWinmain 入口
使用 set_target_properties 即可
set_target_properties(my_project PROPERTIES WIN32_EXECUTABLE $<$<CONFIG:Release>:TRUE>)
$<$<CONFIG:Release>:TRUE> 表示只有 Release 下才为 TRUE,其余默认为 FALSE,FALSE 就是默认使用 Console
如果你需要在编译完后将 exe 依赖的三分库的 dll 挪到特定文件夹下,可以使用 add_custom_command 实现
在 vs 中,我们可以使用 Configuration Properties->Build Events->Post-Build Event,在 Command Line 输入 for 循环复制的伪代码
但是 CMake 中我们则需要上面提到的方法
set(DLL_FILES
${ADir}/a.dll
${BDir}/b.dll
${CDir}/c.dll
) foreach(DLL ${DLL_FILES})
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_custom_command(TARGET my_project POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${DLL} ${CMAKE_CURRENT_SOURCE_DIR}/build/bin/$<CONFIG>/
)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
add_custom_command(TARGET my_project POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${DLL} ${CMAKE_CURRENT_SOURCE_DIR}/build/bin/MYPROJECT/${MF_VERSION}/
)
endif()
endforeach()
通过这种方法,在程序编译后会自动将 a.dll b.dll c.dll 复制到 ${CMAKE_CURRENT_SOURCE_DIR}/build/bin/$<CONFIG>/ 文件夹下
如果你需要覆盖某个文件内的内容,比如说版本号,可以借助 file 函数
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(DATA_VERSION_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/bin/MYPROJECT/data/VERSION)
file(WRITE ${DATA_VERSION_PATH} ${MF_VERSION})
message(STATUS "Set build version success.")
endif()
其中 ${MF_VERSION} 是版本号
更新:vcpkg 可以不使用 powershell 脚本添加 vcpkg 选项,而是使用 TRIPLET 实现
cmake .. -DVCPKG_TARGET_TRIPLET=x86-windows-static
结尾:如果有跨平台需求的,可以参考开源项目的 CMakeKLists,比如说 OBS,它支持跨平台的构建,其中包含了很多脚本的编写
CMake 确实复杂,短时间内是无法理解透,起码目前为止很多 CMake 函数以及 CMake 中的脚本编写我还是懵懂状态,后续得继续学习才行啊
Windows 项目的 CMakeLists 编写的更多相关文章
- 一个项目的Makefile编写及调试
父Makefile 在src目录下包含很多文件夹,那么需要遍历所有的目录执行Makefile,那么给一个在src目录下的Makefile. # 需要排除的目录 exclude_dirs := incl ...
- 团队项目——编写项目的Spec
团队项目--编写项目的Spec 一.Spec的目标 spec主要用来说明软件的外部功能,和用户的交互情况,主要用来说明软件内部的设计.图片编辑器是与生活息息相关的一个必备软件,随的流行, ...
- Windows 下的 Makefile 编写
Windows 下的 Makefile 编写(一)Makefile的基本规则 作者:cntrump Makefile对于很多人来说是陌生的,特别是习惯于使用 IDE 的人来说,似乎没有听说过 Make ...
- 如何在我自己的web 项目的jsp页面中添加链接,直接让别人通过内网在我的电脑上下载文件
今天接到一个任务,将昨天年会的视频,音频,图片等放在公司自己的服务器上,使连接同一个路由器的(即同一个内网)的同事可以通过内网下载视频(通过内网下载,可以提高下载速度). 备注:本次用的是tomcat ...
- 09_Android中ContentProvider和Sqllite混合操作,一个项目调用另外一个项目的ContentProvider
1. 编写ContentPrivider提供者的Android应用 清单文件 <?xml version="1.0" encoding="utf-8"? ...
- mavean项目的jar位置的影响
由于项目的数据库需求改变了,有mysql数据库变为oracle的,那么对于项目就是需要改变数据库连接池.这个项目运用了mavean框架,那么下载jar在pom.xml文件中填写就可以了,但是oracl ...
- 【Devops】【docker】【CI/CD】Jenkins源码管理,设置gitlab上项目的clone地址 + jenkins构建报错:Please make sure you have the correct access rights and the repository exists.
注意,如果 jenkins构建报错:Please make sure you have the correct access rights and the repository exists. 而此时 ...
- TOMCAT下面发布项目的4种方式
摘要 TOMCAT下面发布项目的4种方式,可用于在平时资料查询. 第一种方法: 将web项目文件件拷贝到webapps 目录中:或者直接通过Eclipse发布到Tomcat上. 第二种方法: 在tom ...
- 在Qt示例项目的C ++ / QML源中的//! [0]的含义是什么?
在Qt示例项目的C ++ / QML源中的//! [0]的含义是什么? 例如: //! [0] GLWidget :: GLWidget(Helper * helper,QWidget * pare ...
- 自动生成项目的Makefile文件
自动生成项目的Makefile文件 理论基础 跟我一起写 Makefile: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=4 ...
随机推荐
- Programming abstractions in C阅读笔记:p84-p87
<Programming Abstractions In C>学习第43天,p84-p87总结. 一.技术总结 1.record record也称为structure(结构体),是一种数据 ...
- 【pytorch】ResNet源码解读和基于迁移学习的实战
"工欲善其事,必先利其器",掌握ResNet网络有必要先了解其原理和源码.本文分别从原理.源码.运用三个方面出发行文,先对ResNet原理进行阐述,然后对pytorch中的源码进行 ...
- Linux第四章(80X86保护模式及其编程)
80X86保护模式及其编程 80X86基础知识 保护模式内存管理 各种保护措施 中断和异常处理 任务管理 保护模式编程的初始化 一个简单的多任务内核 4.1 80X86系统寄存器和系统指令 为了协助处 ...
- 【page cache】回写机制
目录 writeback 回写 相关结构体 底层设备信息 初始化 部分字段说明 设备回写管理 初始化 部分字段说明 回写任务 部分字段说明 回写线程 初始化 立即唤醒 wb_wakeup wb_que ...
- API接口设计规范
说明:在实际的业务中,难免会跟第三方系统进行数据的交互与传递,那么如何保证数据在传输过程中的安全呢(防窃取)?除了https的协议之外,能不能加上通用的一套算法以及规范来保证传输的安全性呢? 下面我们 ...
- 织梦tag怎么显示每个tag相应的文章数量
有些时候我们想实现类似于wordpress那样的tag,就是在显示tag的链接和tag名的同时,还能显示每个tag关联的文章的数量.如下图所示: 这就需要修改/include/taglib/tag.l ...
- 【Python】代理池针对ip拦截破解
代理池是一种常见的反反爬虫技术,通过维护一组可用的代理服务器,来在被反爬虫限制的情况下,实现数据的爬取.但是,代理池本身也面临着被目标网站针对ip进行拦截的风险. 本文将详细介绍代理池针对ip拦截破解 ...
- 一个关于 i++ 和 ++i 的面试题打趴了所有人
前言 都说大城市现在不好找工作,可小城市却也不好招人. 我们公司招了挺久都没招到,主管感到有些心累. 我提了点建议,是不是面试问的太深了,在这种小城市,能干活就行. 他说自己问的面试题都很浅显,如果答 ...
- Flask框架——Flask脚本、flask知识点补充
文章目录 Flask_脚本 1 集成Python shell 1.1 flask-script的用法: 1.1.1 实例:flask-script的简单实现 1.1.1命令添加方式: 第一种(无参命令 ...
- 将python程序打包为exe可执行文件方法
将py打包为exe文件需要依赖pyinstaller第三方库 -F:打包后只生成单个exe格式文件: -D:默认选项,创建一个目录,包含exe文件以及大量依赖文件: -c:默认选项,使用控制台(就是类 ...