双笙子佯谬老师的【公开课】现代CMake高级教程课程笔记
第 4 章:对象的属性
除了 POSITION_INDEPENDENT_CODE 还有哪些这样的属性?
add_executable(main main.cpp)
set_property(TARGET main PROPERTY CXX_STANDARD 17) # 采用 C++17 标准进行编译(默认 11)
set_property(TARGET main PROPERTY CXX_STANDARD_REQUIRED ON) # 如果编译器不支持 C++17,则直接报错(默认 OFF)
set_property(TARGET main PROPERTY WIN32_EXECUTABLE ON) # 在 Windows 系统中,运行时不启动控制台窗口,只有 GUI 界面(默认 OFF)
set_property(TARGET main PROPERTY LINK_WHAT_YOU_USE ON) # 告诉编译器不要自动剔除没有引用符号的链接库(默认 OFF)
set_property(TARGET main PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置动态链接库的输出路径(默认 ${CMAKE_BINARY_DIR})
set_property(TARGET main PROPERTY ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置静态链接库的输出路径(默认 ${CMAKE_BINARY_DIR})
set_property(TARGET main PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) # 设置可执行文件的输出路径(默认 ${CMAKE_BINARY_DIR})
set_target_properties 批量设置多个属性
add_executable(main main.cpp)
set_target_properties(main PROPERTIES
CXX_STANDARD 17 # 采用 C++17 标准进行编译(默认 11)
CXX_STANDARD_REQUIRED ON # 如果编译器不支持 C++17,则直接报错(默认 OFF)
WIN32_EXECUTABLE ON # 在 Windows 系统中,运行时不启动控制台窗口,只有 GUI 界面(默认 OFF)
LINK_WHAT_YOU_USE # 告诉编译器不要自动剔除没有引用符号的链接库(默认 OFF)
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib # 设置动态链接库的输出路径(默认 ${CMAKE_BINARY_DIR})
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib # 设置静态链接库的输出路径(默认 ${CMAKE_BINARY_DIR})
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin # 设置可执行文件的输出路径(默认 ${CMAKE_BINARY_DIR})
)
通过全局的变量,让之后创建的所有对象都享有同样的属性
相当于改变了各个属性的初始默认值。要注意此时 set(CMAKE_xxx) 必须在 add_executable 之前才有效
set(CMAKE_CXX_STANDARD 17) # 采用 C++17 标准进行编译(默认 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 如果编译器不支持 C++17,则直接报错(默认 OFF)
set(CMAKE_WIN32_EXECUTABLE ON) # 在 Windows 系统中,运行时不启动控制台窗口,只有 GUI 界面(默认 OFF)
set(CMAKE_LINK_WHAT_YOU_USE ON) # 告诉编译器不要自动剔除没有引用符号的链接库(默认 OFF)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置动态链接库的输出路径(默认 ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置静态链接库的输出路径(默认 ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) # 设置可执行文件的输出路径(默认 ${CMAKE_BINARY_DIR})
add_executable(main main.cpp)
不要使用编译器限定关键字
如果你从百度学的 CMake,你可能会犯如下的错误
对于 CXX_STANDARD 这种 CMake 本就提供了变量来设置的东西,就不要自己去设置 -std=c++17 选项,会和 CMake 自己设置好的冲突,导致出错。请始终用 CXX_STANDARD 或是全局变量 CMAKE_CXX_STANDARD 来设置 -std=c++17 这个 flag,CMake 会在配置阶段检测编译器是否支持 C++17。CUDA 的 -arch=sm_75 也是同理,请使用 CUDA_ARCHITECTURES 属性。
再说了 -std=c++17 只是 GCC 编译器的选项,无法跨平台用于 MSVC 编译器。
使用动态链接库
假如你一定要用动态链接库(Windows 对动态链接很不友好)
mylib/mylib.cpp
#include <cstdio>
#ifdef _MSC_VER
__declspec(dllexport)
#endif
void say_hello()
{
printf("Hello, world!\n");
}
mylib/mylib.h
#pragma once
#ifdef _MSC_VER
__declspec(dllexport)
#endif
void say_hello();
mylib/CMakeLists.txt
add_library(mylib SHARED mylib.cpp mylib.h)
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
add_subdirectory(mylib)
add_executable(main main.cpp)
target_link_libraries(main PUBLIC mylib)
常见问题:链接了自己的 dll,但是为什么运行时会找不到?
这是因为你的 dll 和 exe 不在同一目录。Windows 比较蠢,他只会找当前 exe 所在目录,然后查找 PATH,找不到就报错。而你的 dll 在其他目录,因此 Windows 会找不到 dll。
解决1:把 dll 所在位置加到你的 PATH 环境变量里去,一劳永逸。
解决2:把这个 dll,以及这个 dll 所依赖的其他 dll,全部拷贝到和 exe 文件同一目录下。
手动拷贝 dll 好麻烦,能不能让 CMake 把 dll 自动生成在 exe 同一目录?
归根到底还是因为 CMake 把定义在顶层模块里的 main 放在 build/main.exe。而 mylib 因为是定义在 mylib 这个子模块里的,因此被放到了 build/mylib/mylib.dll。
解决 1:设置 mylib 对象的 xx_OUTPUT_DIRECTORY 系列属性
所以,可以设置 mylib 的这些属性,让 mylib.dll 文件输出到 PROJECT_BINARY_DIR,也就是项目根目录(main 所在的位置)。这样 main.exe 在运行时就能找到 mylib.dll 了。
是的,为了伺候这睿智的 Wendous 系统,需要设置全部 6 个属性,是不是非常繁琐?
mylib/CMakeLists.txt
add_library(mylib SHARED mylib.cpp mylib.h)
set_property(TARGET mylib PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_property(TARGET mylib PROPERTY ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_property(TARGET mylib PROPERTY LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_property(TARGET mylib PROPERTY RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR})
set_property(TARGET mylib PROPERTY ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR})
set_property(TARGET mylib PROPERTY LIBRARY_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR})
set_property(TARGET mylib PROPERTY RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR})
set_property(TARGET mylib PROPERTY ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR})
set_property(TARGET mylib PROPERTY LIBRARY_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR})
而 Linux 系统支持 RPATH,CMake 会让生成出来可执行文件的 RPATH 字段指向他链接了的 .so 文件所在目录,运行时会优先从 RPATH 里找链接库,所以即使不在同目录也能找到。
所以还有第三种解决方案:微软,我卸卸你全家(指卸载)。然后安装 Arch Linux 系统。
需要手动修改或查看一个 ELF 文件的 RPATH,可以用 chrpath 或 patchelf 命令。
❯ chrpath -l build/main
build/main: RUNPATH=/mnt/h/Code/lessonCode/CMakeLession/build
❯ ldd build/main
linux-vdso.so.1 (0x00007ffebc63c000)
libmylib.so => /mnt/h/Code/lessonCode/CMakeLession/build/libmylib.so (0x00007fa063d5b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa063b29000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa063d67000)