我正在 CMake 中构建一个 static 库,它依赖于许多其他静态库。我希望它们全部包含在输出 .lib/.a 文件中,这样我就可以向客户发送一个大的 lib 文件。在 Visual Studio 2010 中,有一个选项“链接库依赖项”,它正是执行此操作。
但我找不到如何在 CMake 中做到这一点。您可以通过 CMake 设置此标志,或者以其他方式获得相同的结果吗?我已经尝试过 target_link_libraries(...) 和 add_dependency(...),但 CMake 似乎只是忽略静态库的这一行。
好吧,我有一个解决方案。首先,重要的是要认识到静态库不会将其他静态库链接到代码中。必须创建一个组合库,在 Linux 上可以使用
ar
来完成。有关更多信息,请参阅将静态库链接到其他静态库。
考虑两个源文件:
int hi()
{
return 0;
}
int bye()
{
return 1;
}
CMakeLists.txt
文件是创建两个库,然后创建一个组合库,如下所示:
project(test)
add_library(lib1 STATIC test1.c)
add_library(lib2 STATIC test2.c)
add_custom_target(combined ALL
COMMAND ${CMAKE_AR} rc libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)
在这种情况下,
ar
命令的选项与平台相关,尽管CMAKE_AR
变量与平台无关。我会四处看看是否有更通用的方法来做到这一点,但这种方法适用于使用 ar
的系统。
基于如何设置 CMAKE_AR 的选项?,看起来更好的方法是:
add_custom_target(combined ALL
COMMAND ${CMAKE_CXX_ARCHIVE_CREATE} libcombined.a $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>)
这应该是平台无关的,因为这是 CMake 用于在内部创建存档的命令结构。当然,前提是您想要传递给存档命令的唯一选项是
rc
,因为这些选项已硬连线到 CMake 的 ar
命令中。
我想通过提供我的
CMakeLists.txt
文件来增强其他解决方案,该文件实际上也可以在构建依赖项方面发挥作用。
滥用CMake的解决方案
cmake_minimum_required(VERSION 2.8)
add_library(lib1 test1.cpp)
add_library(lib2 test2.cpp)
include_directories(${CMAKE_CURRENT_DIR})
add_executable(mainexec main.cpp)
target_link_libraries(mainexec combinedLib) # Important to place before add_custom_target
set(LIBNAME "combinedLib.lib")
add_custom_command(
OUTPUT ${LIBNAME}
COMMAND lib.exe /OUT:${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
DEPENDS lib1 lib2
COMMENT "Combining libs..."
)
add_custom_target(combinedLib
DEPENDS ${LIBNAME}
)
请注意,到目前为止,该解决方案适用于 Visual Studio,但我想它可以兼容多平台。我可以想象以下版本可能适用于基于 Unix 的平台:
set(LIBNAME "libCombinedLib.a")
add_custom_command(
OUTPUT ${LIBNAME}
COMMAND ar -rcT ${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
DEPENDS lib1 lib2
COMMENT "Combining libs..."
)
请注意,这些解决方案在某种程度上滥用了 CMake,因为如果您在
target_link_libraries
声明之后放置 add_custom_target
调用,它会抱怨 UTILITY 类型的目标(而不是 STATIC 或 SHARED)。
CMake 符合目标声明的解决方案
为了使其符合 CMake 标准,您可以将 `target_link_libraries' 调用替换为
target_link_libraries(mainexec ${LIBNAME})
add_dependencies(mainexec combinedLib)
就我而言,这并不完全令人满意,因为
mainexec
必须了解 combinedLib
,尽管它期望所有依赖项都由 target_link_libraries
调用处理。
耦合较少的替代解决方案
进一步研究导入的目标,我最终找到了解决我最后一个问题的解决方案:
cmake_minimum_required(VERSION 2.8)
add_library(lib1 test1.cpp)
add_library(lib2 test2.cpp)
include_directories(${CMAKE_CURRENT_DIR})
add_executable(mainexec main.cpp)
set(LIBNAME "combinedLib.lib")
add_custom_command(
OUTPUT ${LIBNAME}
COMMAND lib.exe /OUT:${LIBNAME} $<TARGET_FILE:lib1> $<TARGET_FILE:lib2>
DEPENDS lib1 lib2
COMMENT "Combining libs..."
)
add_custom_target(combinedLibGenerator
DEPENDS ${LIBNAME}
)
add_library(combinedLib STATIC IMPORTED)
set_property(TARGET combinedLib PROPERTY IMPORTED_LOCATION ${LIBNAME})
add_dependencies(combinedLib combinedLibGenerator)
target_link_libraries(mainexec combinedLib)
如果您打算模块化整个,请在
GLOBAL
之后添加 STATIC IMPORTED
以使导入的目标全局可见。
便携式 CMake 解决方案
在当前的 CMake 版本中,CMake 提供了对传递依赖项和接口库的全面支持。然后,接口库可以与其他库“链接”,而该接口库又可以与其他库“链接”。为什么要加引号?虽然这效果很好,但实际上并没有创建物理的组合库,而是为“子库”集创建了一种别名。这仍然是我们最终需要的解决方案,这就是为什么我想在此处添加它。
add_library(combinedLib INTERFACE)
target_link_libraries(combinedLib INTERFACE lib1 lib2)
target_link_libraries(mainexec combinedLib)
就是这样!
没有像之前的答案那样手动目标命名:
add_library(bcd STATIC dummy.c)
set_property(TARGET bcd PROPERTY
STATIC_LIBRARY_OPTIONS "$<TARGET_OBJECTS:abc>;${LIBSTDCXX};${LIBGCC}"
)
另一种解决方案(不需要手动设置目标名称,但它会使对象数量增加一倍):
set_property(TARGET abc PROPERTY
STATIC_LIBRARY_OPTIONS "$<TARGET_OBJECTS:abc>;${LIBSTDCXX};${LIBGCC}"
)
或者(前置依赖项):
set_property(TARGET abc PROPERTY
STATIC_LIBRARY_OPTIONS "${LIBSTDCXX};${LIBGCC}"
)
或者(将依赖项添加到新目标):
add_library(bcd STATIC dummy.c)
set_property(TARGET bcd PROPERTY
STATIC_LIBRARY_OPTIONS "$<TARGET_OBJECTS:abc>;${LIBSTDCXX};${LIBGCC}"
)