如何指定给定DLL的输出目录?

Bor*_*ein 8 windows dll cmake shared-libraries

我正在使用以下内容src/CMakeLists.txt

cmake_minimum_required(VERSION 3.1.0)
project(foo)
add_library(foo SHARED foo.cpp)
set_target_properties(foo
    PROPERTIES
        LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/subdir
)
Run Code Online (Sandbox Code Playgroud)

在 Windows 上,我使用以下方法构建库:

mkdir build
cd build
cmake ../src
cmake --build .
Run Code Online (Sandbox Code Playgroud)

输出文件: ~/build/Debug/foo.dll

预期输出文件: ~/build/Debug/subdir/foo.dll

我究竟做错了什么?

它在 Windows 以外的平台上运行良好,并且似乎应该根据以下文档运行:

Bor*_*ein 18

简短回答

在 Windows 上,与其他平台不同,您应该使用RUNTIME_OUTPUT_DIRECTORY而不是LIBRARY_OUTPUT_DIRECTORY指定共享库的输出目录。

长答案

有关输出工件的 CMake 文档对此进行了记录:

运行时输出工件

构建系统目标的运行时输出工件可能是:

  • 由add_executable() 命令创建的可执行目标的可执行文件(例如.exe)。
  • 在 DLL 平台上:由带有 SHARED 选项的 add_library() 命令创建的共享库目标的可执行文件(例如 .dll) 。RUNTIME_OUTPUT_DIRECTORY 和 RUNTIME_OUTPUT_NAME 目标属性可用于控制构建树中的运行时输出工件位置和名称。

库输出工件

构建系统目标的库输出工件可能是:

  • 由带有 MODULE 选项的 add_library() 命令创建的模块库目标的可加载模块文件(例如 .dll 或 .so)。
  • 在非 DLL 平台上:由带有 SHARED 选项的 add_library() 命令创建的共享库目标的共享库文件(例如 .so 或 .dylib)。LIBRARY_OUTPUT_DIRECTORY 和 LIBRARY_OUTPUT_NAME 目标属性可用于控制构建树中的库输出工件位置和名称。

但为什么 CMake 会在 DLL 平台(Windows)和非 DLL 平台(macOS、Linux 等)之间产生如此大的差异?

我找不到记录此设计决策的源代码,但我相信其基本原理是 Windows 不支持 的概念rpath,即.exe文件无法在内部存储其依赖.dll文件的位置。因此,在 Windows 上,.dll文件通常存储在与.exe文件相同的文件夹中,以确保在运行时可以找到 DLL。相反,在 Unix 系统上,共享库文件通常存储在单独的lib文件夹中,而应用程序二进制文件存储在bin文件夹中,这不是问题,因为二进制文件可以使用rpath.

总之,跨平台开发定义LIBRARY_OUTPUT_DIRECTORY和是有意义的RUNTIME_OUTPUT_DIRECTORY,如下所示:

cmake_minimum_required(VERSION 3.1.0)
project(foo)
add_library(foo SHARED foo.cpp)
set_target_properties(foo
    PROPERTIES
        LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/lib
        RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/bin
)
Run Code Online (Sandbox Code Playgroud)