60

我有以下布局:

top_project
    + subproject1
    + subproject2

每个subproject1subproject2创建一个静态库。我想将这些静态库链接到该top_project级别的单个共享库中。

到目前为止我收集的信息是:

  • 要么编译使用-fPic(除了 Windows 之外的所有东西都是必需的),以便创建与位置无关的代码,这将允许将静态库链接到单个共享库中,或者解压缩所有静态库(例如 using ar)并将它们重新链接到共享库中(其中我认为这是一个不优雅且不可移植的解决方案)
  • 所有源文件都必须明确地提供给add_library命令:由于某种我无法理解的原因,简单地编写add_library(${PROJECT_NAME} SHARED subproject1 subproject2)不能按预期工作(它实际上创建了一个空库并且没有正确注册依赖项)
  • CMake 中有一个 OBJECT 库功能,但我认为它的目的并不是真正做我想做的事。

有什么想法吗?

4

6 回答 6

37

好吧,我想通了:这比它应该的要痛苦得多。直到最近,Kitware 的人们才明白为什么有人会想要从静态库创建 DLL。他们的论点是在主目录(例如在我的例子中)中应该总是有源文件,top_project因为它实际上是一个自己的项目。我对事情的看法不同,我需要分解top_project成不应该独立存在的较小的子项目(即,为他们创建一个成熟的项目并使用添加它们是没有意义的ExternalProject_Add)。此外,当我发布我的共享库(用于使用,例如与 Java 本地接口)时,我不想发布数十个共享库,因为这等于暴露了我项目的内部布局。无论如何,我认为已经为从静态库创建共享库做了一个案例,我将继续讨论技术细节。

在 和 的 CMakeLists.txt 中subproject1subproject2您应该使用 OBJECT 库功能(在 CMake 2.8.8 中引入)创建目标:

add_library(${PROJECT_NAME} OBJECT ${SRC})

whereSRC指定源文件列表(注意这些应该在 CMakeLists.txt 文件中明确设置,因为它允许 make 在检测到 CMakeLists.txt 修改时重新启动 CMake,例如添加或删除文件时)

在 中top_project,使用以下命令添加子项目:

add_subdirectory(subproject1)
add_subdirectory(subproject2)

要查看静态库中的符号,请使用:

set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols")

然后,您可以使用以下方法创建共享库:

add_library(${PROJECT_NAME} SHARED $<TARGET_OBJECTS:subproject1>
                                   $<TARGET_OBJECTS:subproject2>)

我发现任何“普通”库(即不是对象)都需要在单独的add_library命令中添加,否则它会被忽略。

对于可执行文件,您可以使用:

add_executable(name_of_executable $<TARGET_OBJECTS:subproject1>
                  $<TARGET_OBJECTS:subproject2>)
set(LINK_FLAGS ${LINK_FLAGS} "-Wl,-whole-archive")
target_link_libraries(name_of_executable ${PROJECT_NAME}

我再说一遍,这仅适用于 CMake 2.8.8 版。同样,CMake 非常好地管理依赖关系并且是跨平台的,因为它并不比普通的旧 Makefile 痛苦得多,而且灵活性肯定更低。

于 2012-07-12T09:31:10.060 回答
10

我的解决方案是简单地将/WHOLEARCHIVE, -all_load, 或添加--whole-archive到链接器标志中,这样当您的主库被链接时,所有子库都包括在内,包括它们的所有符号(默认行为是只包括子库的符号由主库使用。例如:

源文件

$ echo "void Func1() { }" > source1.cpp
$ echo "void Func2() { }" > source2.cpp
$ echo "void Func3() { }" > source3.cpp
$ echo "void Func4() { }" > source4.cpp

天真的 CMakeLists.txt

cmake_minimum_required(VERSION 3.7)

# The 'sub' libraries, e.g. from an `add_subdirectory()` call.
add_library(sublib_a STATIC source1.cpp source2.cpp)
add_library(sublib_b STATIC source3.cpp source4.cpp)

# The main library that contains all of the sub libraries.
add_library(mainlib SHARED)

target_link_libraries(mainlib sublib_a sublib_b)

运行它(在 OSX 上):

$ make VERBOSE=1
...
[100%] Linking CXX shared library libmainlib.dylib
/usr/local/Cellar/cmake/3.7.1/bin/cmake -E cmake_link_script CMakeFiles/mainlib.dir/link.txt --verbose=1
/Library/Developer/CommandLineTools/usr/bin/c++   -dynamiclib -Wl,-headerpad_max_install_names  -o libmainlib.dylib -install_name @rpath/libmainlib.dylib  libsublib_a.a libsublib_b.a 
[100%] Built target mainlib

$ nm libmainlib.dylib | grep Func
$

正确的 CMakeLists.txt

附加这个:

# By default, symbols provided by the sublibs that are not used by mainlib (which is all of them in this case)
# are not used. This changes that.
if (WIN32)
    set_target_properties(mainlib PROPERTIES
        LINK_FLAGS "/WHOLEARCHIVE"
    )
elseif (APPLE)
    set_target_properties(mainlib PROPERTIES
        LINK_FLAGS "-Wl,-all_load"
    )
else ()
    set_target_properties(mainlib PROPERTIES
        LINK_FLAGS "-Wl,--whole-archive"
    )
endif ()

运行它(注意额外的-all_load):

$ make VERBOSE=1
[100%] Linking CXX shared library libmainlib.dylib
/usr/local/Cellar/cmake/3.7.1/bin/cmake -E cmake_link_script CMakeFiles/mainlib.dir/link.txt --verbose=1
/Library/Developer/CommandLineTools/usr/bin/c++   -dynamiclib -Wl,-headerpad_max_install_names -Wl,-all_load -o libmainlib.dylib -install_name @rpath/libmainlib.dylib  libsublib_a.a libsublib_b.a 
[100%] Built target mainlib

$ nm libmainlib.dylib | grep Func
0000000000001da0 T __Z5Func1v
0000000000001db0 T __Z5Func2v
0000000000001dc0 T __Z5Func3v
0000000000001dd0 T __Z5Func4v

-all_load请注意,到目前为止,我只进行了实际测试,并且/WHOLEARCHIVE是 MSVC 2015 选项。

于 2017-01-25T15:00:13.350 回答
4

另一种方法。

这种方式看起来更简单,但我不确定它有多完美:

https://stackoverflow.com/a/14347487/602340

于 2013-01-15T21:44:29.867 回答
2

另一种方法是提供所有项目的源文件和头文件的路径,并将它们构建在一起以生成 .so 。这通常是推荐的方式,而不是创建静态库,然后从中创建共享库。

基本上你应该做以下事情:

FILE(GLOB subproject1_sources
  <sub_project1_lib_sources_dir>/file1.c
  <sub_project1_lib_sources_dir>/file2.c //... etc
)

FILE(GLOB subproject2_sources
  <sub_project2_lib_sources_dir>/file1.c
  <sub_project2_lib_sources_dir>/file2.c //... etc
)

FILE(GLOB topProject_sources
  <top_project_lib_sources_dir>/file1.c
  <top_project_lib_sources_dir>/file2.c //... etc
)

include_directories("<sub_project1_lib_sources_dir>")
include_directories("<sub_project2_lib_sources_dir>")
include_directories("<top_project_lib_sources_dir>") //should be "." if you're building from here

add_library(topProject SHARED ${topProject_sources} ${subproject1_sources} ${subproject2_sources})
于 2015-05-29T20:07:08.440 回答
1

我不确定这是否适合您的需求,但 cmake 还提供了INTERFACE库,这些库正是满足(以及其他)这种需求。

add_library(bundle INTERFACE)
target_link_libraries(bundle lib1 lib2)

将 lib1 和 lib2 捆绑到一个库中,并继承and的PUBLICandINTERFACE部分。lib1lib2

更多信息在这里

于 2020-05-01T19:29:25.733 回答
0

将以下宏添加到您的 cmake 脚本中。

MACRO (TARGET_LINK_LIBRARIES_WHOLE_ARCHIVE target)
  IF (WIN32)
    FOREACH (arg IN LISTS ARGN)
      SET_TARGET_PROPERTIES(
        ${target} PROPERTIES LINK_FLAGS "/WHOLEARCHIVE:${lib}"
      )
    ENDFOREACH ()
  ELSE ()
    IF (APPLE)
      SET(LINK_FLAGS "-Wl,-all_load")
      SET(UNDO_FLAGS "-Wl,-noall_load")
    ELSE ()
      SET(LINK_FLAGS "-Wl,--whole-archive")
      SET(UNDO_FLAGS "-Wl,--no-whole-archive")
    ENDIF ()
    TARGET_LINK_LIBRARIES(${target} ${LINK_FLAGS} ${ARGN} ${UNDO_FLAGS})
  ENDIF ()
ENDMACRO ()

然后,在 CMakeLists.txt/*.cmake 中,可以这样使用TARGET_LINK_LIBRARIES_WHOLE_ARCHIVE(target libs...)

于 2021-08-27T12:15:13.017 回答