【问题标题】:Overlapping dependencies between libraries in CMakeCMake中库之间的重叠依赖关系
【发布时间】:2012-04-04 13:54:55
【问题描述】:

假设有以下目录结构:

projects
   |
   +--lib1
   |   |
   |   +-CMakeFiles.txt
   |
   +--lib2
   |   |
   |   +-CMakeFiles.txt
   |
   +--test
       |
       +-CMakeFiles.txt

lib1/CMakeFiles.txt:

cmake_minimum_required(VERSION 2.0)

add_library(lib1 STATIC lib1.cpp)

lib2/CMakeFiles.txt:

cmake_minimum_required(VERSION 2.0)

add_subdirectory(../lib1 ${CMAKE_CURRENT_BINARY_DIR}/lib1)
add_library(lib2 STATIC lib2.cpp)
target_link_libraries(lib2 lib1)

测试/CMakeFiles.txt:

cmake_minimum_required(VERSION 2.0)
project(test)

add_subdirectory(../lib1 ${CMAKE_CURRENT_BINARY_DIR}/lib1)
add_subdirectory(../lib2 ${CMAKE_CURRENT_BINARY_DIR}/lib2)

add_executable(test main.cpp)
target_link_libraries(test lib1 lib2)

lib2 取决于 lib1test 取决于它们两者。 (我知道技术上的静态库不会“链接”,但这只是一个例子。)

问题在于,在当前设置下,lib1 编译两次 - 第一次在“test”构建目录中,第二次在“test/build_directory/lib2/build_directory”中。我想避免这种情况。

我希望能够将对 lib1、lib2 或两者的依赖项(使用 add_subdirectory)添加到位于其他地方的任何项目中。所以移动 CMakeFiles 不是一种选择。我还想避免多次编译任何库。

我该怎么做?

平台:CMake v. 2.8.4 和 Windows XP SP3

顶级 CMakeLists.txt 文件不是一个选项,因为我想保持一个干净的顶级目录,并能够在其他项目中包含可以位于其他位置的库。因为它是 Windows,所以我不能“在系统范围内安装包”——我不想失去即时切换编译器的能力。使用不同编译器构建的实用程序库将使用不同的 C 运行时库/ABI,因此将不兼容。

【问题讨论】:

    标签: cmake


    【解决方案1】:

    另一种解决方案是在子目录的顶部添加一个守卫-CMakeLists.txt:

    if(TARGET targetname)
        return()
    endif(TARGET targetname)
    

    这将导致 cmake 在第二次添加子目录时不执行任何操作(当然,如果在该文件中定义了 targetname)。

    这将导致 lib beeing 在 build/ 树中的任意位置(取决于首先添加它的模块)构建,但它只会构建一次并链接到任何地方。

    在您的示例中,您将添加

    if(TARGET lib1)
        return()
    endif(TARGET lib1)
    

    在 lib1/CMakeFiles.txt 的顶部

    【讨论】:

    • 不幸的是,在 CMake 版本 3.13 中,此解决方案不起作用。 CMake 停止并显示指向 add_subdirectory 调用(试图包含 subdirectory-CMakeLists.txt 文件)本身的错误消息。解决方法是在 add_subdirectory 调用周围放置警卫:if (NOT TARGET lib1) add_subdirectory(lib1) endif()
    【解决方案2】:

    使用 CMake,库依赖是传递的,所以你不应该在 test/CMakeFiles.txt 中调用 add_subdirectory 两次(也不需要将 lib1 列为 test 的依赖,因为它已经是 @ 的依赖987654325@'s)。

    所以你可以将test的CMakeFiles.txt修改为:

    cmake_minimum_required(VERSION 2.8.7)  # Prefer the most current version possible
    project(test)
    
    add_subdirectory(../lib2 ${CMAKE_CURRENT_BINARY_DIR}/lib2)
    
    add_executable(test main.cpp)
    target_link_libraries(test lib2)
    

    此外,您可能应该从非项目 CMakeFiles.txt 文件(lib 文件)中删除 cmake_minimum_required 调用。如需更多信息,请运行:

    cmake --help-policy CMP0000
    


    如果您添加依赖于lib1lib2 的类似test2 子目录和项目,此设置仍将导致重新编译所有库。如果你真的不想在projects/ 中有一个顶级的 CMakeFiles.txt,那么你会被困在你正在做的事情上,或者你可以使用exportinstall 命令。

    export 将创建一个文件,该文件可能是其他项目的included,并将目标导入到调用include 的项目中。

    install 可以将库安装到projects/ 的另一个公共子目录中。根据您的源目录结构,这可能具有仅使预期的库 API 标头可用于依赖项目的好处。

    但是,如果修改了这两个选项,则需要重新构建(和安装)依赖库项目,而您当前的设置包括项目中的所有依赖目标,因此对依赖库中源文件的任何更改将导致您的 test 目标过期。

    有关exportinstall 的更多详细信息,请运行:

    cmake --help-command export
    cmake --help-command install
    

    【讨论】:

    • 在某些情况下,您可以将依赖项 lib1 唯一分配给 lib2,反之亦然。但也有一些常见的情况是,您想要构建松散耦合的模块(考虑不同的插件)共享依赖项。因此,例如,您有 prog1,它具有依赖项 lib1 和 lib2,而 prog2 具有依赖项 lib2 和 lib3。现在,如果您想拥有不同的组合(例如,在这种情况下您不需要 prog2,所以您只是不包含它),那么如果 lib2 是 prog2 的依赖项,则可能会出现问题,因为它不再可用.
    • user518450 有一个有效点;在我的情况下,我的应用程序(在示例中称为 test)和 lib2 都包含使用 lib1 的代码,但从 lib2 到 lib1 的依赖是有条件的,因为它取决于 lib2 中启用的功能。所以 lib2 可能会或可能不会依赖于 lib1。不用说,我很想找到解决办法。
    • @psyill 在下面使用标头保护检查我的答案。在这种情况下,它就像一种魅力。
    【解决方案3】:

    也许在您的项目目录中添加一个顶级 CMakeLists.txt。比如:

    project( YourProjects )
    
    add_subdirectory( lib1 )
    add_subdirectory( lib2 )
    add_subdirectory( test )
    

    这应该足够了,并且会在您的顶级构建目录中为您提供解决方案文件或生成文件。然后,您应该从您的 lib1 和 lib2 项目中删除 add_subdirectory( ../lib1 ...,而只需链接到它们。 CMake 在编译测试时会知道如何找到 lib1 和 lib2。

    即在 lib2 中:

    project( lib2) 
    add_library(lib2 STATIC lib2.cpp)
    target_link_libraries(lib2 lib1)
    

    并在测试中:

    project( test )
    add_executable(test main.cpp)
    target_link_libraries(test lib1 lib2)
    

    额外奖励:您将在 lib2 目录中获得用于构建 lib2(具有依赖 lib1)的 makefile/解决方案文件...

    【讨论】:

    • 顶级 cmakelists 不是一个选项。现在,如果您能告诉 cmake 如何让 cmake 知道其他地方还有另一个项目...
    • 当然,我不知道禁止顶级 cmakelists 的额外要求......你知道你仍然可以在 Windows 上“安装”包,例如通过在运行 CMake 时指定 CMAKE_INSTALL_PREFIX?如有必要,您还可以在安装前缀仍位于默认的“C:\Program Files”时更改它。
    • 您是否考虑过分离所有库和应用程序并编写正确的“BuildConfig.cmake”文件,然后在每个项目中使用 find_package?
    • 我知道我可以安装,但这行不通。 Windows 有几个不同的编译器,它们使用几个不同的 C/CPP 运行时,并且在单个编译器(静态/dll)中有几个不同的运行时。因此,Windows 平台上的主要问题是在多个平台上强制兼容。因此,如果您制作一个调用 c/cpp 运行时(new/delete)的实用程序库(statci),将其安装到 programFiles,然后尝试在不同的编译器中使用它,您会遇到问题。我想避免这种情况。 (续)
    • 我想保持当前目录结构,并能够即时更改编译器或重用单个库 ini 多个项目/不同的编译器 - 即使每个项目都必须构建一个库的本地副本。 IE。从具有依赖关系的源代码完全重建。 “find_package”有一点不同的心态(类似于 unix - 构建、安装、到处使用),这不是我想要的。
    猜你喜欢
    • 2011-12-19
    • 2012-02-22
    • 1970-01-01
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多