【问题标题】:Use library with static dependencies in CMAKE在 CMAKE 中使用具有静态依赖项的库
【发布时间】:2023-03-18 12:11:02
【问题描述】:

在 CMake 中有没有办法为具有不同静态依赖项的多个目标使用库? 为了更好地解释它,考虑这个最小的例子:我想要两个可执行文件:第一个应该打印“YES”,第二个应该打印“NO”。为此,我使用库“printsth”,它打印“某物”。它打印的字符串来自“用户”(printyes 或 printno)提供的头文件。这看起来像这样:

├── apps
│   ├── printno
│   │   ├── CMakeLists.txt
│   │   │       add_executable(printno main.cpp)
│   │   │       target_link_libraries(printno PRIVATE printsth)
│   │   │
│   │   ├── main.cpp
│   │   │       #include "printsth/printsth.h"
│   │   │       
│   │   │       int main() {
│   │   │         printsth();
│   │   │         return 0;
│   │   │       }
│   │   │
│   │   └── print_usr.h
│   │           #define USR_STRING  "NO"
│   │
│   └── printyes
│   │   ├── CMakeLists.txt
│   │   │       add_executable(printyes main.cpp)
│   │   │       target_link_libraries(printyes PRIVATE printsth)
│   │   │
│   │   ├── main.cpp
│   │   │       #include "printsth/printsth.h"
│   │   │       
│   │   │       int main() {
│   │   │         printsth();
│   │   │         return 0;
│   │   │       }
│   │   │
│   │   └── print_usr.h
│   │           #define USR_STRING  "YES"
│   │
├── extern
│   └── printsh
│       ├── include
│       │   └── printsh
│       │       └── printsh.h
│       │               void printsth();
│       │
│       ├── src
│       │    ├── CMakeLists.txt
│       │    │       add_library(printsth printsth.cpp)
│       │    │       target_include_directories(printsth PUBLIC ../include)
│       │    │
│       │    └── printsh.cpp
│       │            #include "printsth/printsth.h"
│       │            #include "print_usr.h"
│       │            #include <iostream>
│       │            
│       │            void printsth() {
│       │              std::cout << USR_STRING << std::endl;
│       │            }
│       │     
│       └── CMakeLists.txt
│               cmake_minimum_required(VERSION 3.11...3.16)
│               
│               project(printsh
│                   VERSION 0.1
│                   DESCRIPTION "Print something"
│                   LANGUAGES CXX)
│               
│               add_subdirectory(src)
│        
└── CMakeLists.txt
        cmake_minimum_required(VERSION 3.11...3.16)

        project(printexamples
            VERSION 0.1
            DESCRIPTION "Print examples"
            LANGUAGES CXX)

        add_subdirectory(apps/printyes)
        add_subdirectory(apps/printno)
        add_subdirectory(extern/printsth)

在构建时我显然得到了错误

fatal error: print_usr.h: No such file or directory

那么我可以告诉 CMake 在为 printno 构建 printsh lib 时使用 apps/printno 作为包含目录并在为 printyes 构建时使用 apps/printyes 作为包含目录的任何更改吗?

我知道这个例子没有多大意义,并且很容易摆脱标题依赖项(例如,将自定义字符串作为参数传递给 printsth())并且一切正常。所以这只是一个演示“现实世界”问题的示例,我无法轻易摆脱依赖关系。

【问题讨论】:

  • 你的意思是像 cmake 中的 if else 一样吗?
  • 库不是“为...构建”的,它只是构建一次,然后与其他模块链接在一起。 CMake 不会改变库的工作方式。
  • 欢迎来到 Stack Overflow!这是一个很好的第一个问题!除了完全重新设计以消除依赖关系之外,一种方法是创建printsh 库的“是”和“否”版本。然后,将库的正确“是”或“否”版本链接到 printyesprintno
  • 感谢您的 cmets。我看到它实际上不是一个真正的静态库。 @squareskittles问题是,在“现实”中,这可能是很多不同的版本,我想让libraray(printsth)独立于配置。另一种方法是将库源直接包含到可执行文件中,但我看不到任何方法仍然能够控制库源文件的包含路径等。

标签: c++ cmake header dependencies static-libraries


【解决方案1】:

在 CMake 中,库目标表示编译一次但可以链接到多个目标(可执行文件或其他库)的库。

因为您无法将两个宏定义编译到单个库中,您需要为每个定义集创建一个不同的库目标。也就是说,在您的情况下,您需要创建 两个库

对于重复的命令序列,CMake 提供了宏和函数,并使用不同的参数多次调用它们。

您也可以创建一个“参数化”CMakeLists.txt,并多次“调用”它(通过add_subdirectory):

extern/printsh/src/CMakeLists.txt

# Requires 'printsth_target' and 'printish_usr_dir' variables to be set.
# The first variable denotes name of the target created,
# the second variable denotes include directory with 'print_usr.h' header.
if (NOT printsth_target)
  message(FATAL_ERROR "printsth_target variable is required but is not set.")
endif()
if (NOT printish_usr_dir)
  message(FATAL_ERROR "printish_usr_dir variable is required but is not set.")
endif()

# Create a library target with user-provided name
add_library(${printsth_target} printsth.cpp)
target_include_directories(${printsth_target} PUBLIC ../include)
# Add user-provided include directory
target_include_directories(${printsth_target} PRIVATE ${printish_usr_dir})

使用这样的脚本,printsth 库可以在应用程序的CMakeLists.txt实例化

apps/printno/CMakeLists.txt

add_executable(printno main.cpp)
# Instantiate 'printsth' library with needed parameters.
set(printsth_target "printsh_no")
set(printish_usr_dir ${CMAKE_CURRENT_SOURCE_DIR})
# Need to specify the second argument - build directory, where the library will be built.
add_subdirectory(../../extern/printsh printsh_no_dir)
# No we can link with a library. Use a name, specified for 'printsth_target' variable.
target_link_libraries(printno PRIVATE printsh_no)

应修改顶级CMakeLists.txt 以不实例化库。

CMakeLists.txt

# <...>

add_subdirectory(apps/printyes)
add_subdirectory(apps/printno)
# Uncommenting the following line causes FATAL_ERROR triggered.
#add_subdirectory(extern/printsth)

【讨论】:

  • 感谢您的详细解答!这正是我想要的。我唯一不明白的部分是 add_subdirectory() 做了什么以及为什么它是必要的(如果我构建一个标准的、非参数化的库,我不必使用 add_subdirectory())。
  • 在您的原始代码中,您使用根CMakeLists.txt 中的add_subdirectory(extern/printsth) 来实例化(构建)库。在我的变体中,apps/printno/CMakeLists.txtapps/printyes/CMakeLists.txt 中有两个这样的调用(后一个文件未显示,因为它与前者相似)。
猜你喜欢
  • 2012-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 2011-06-06
  • 1970-01-01
相关资源
最近更新 更多