是否有可能让CMake构建同一个库的静态版本和共享版本?

gct*_*gct 125 static shared cmake

相同的来源,只需要一个静态和共享版本.容易吗?

Chr*_*uns 117

是的,这很容易.只需使用两个"add_library"命令:

add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)
Run Code Online (Sandbox Code Playgroud)

即使您有许多源文件,也可以将源列表放在cmake变量中,因此它仍然很容易.

在Windows上,您应该为每个库指定一个不同的名称,因为共享和静态都有一个".lib"文件.但是在Linux和Mac上,您甚至可以为两个库提供相同的名称(例如libMyLib.a和libMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)
Run Code Online (Sandbox Code Playgroud)

但我不建议同时给出库的静态和动态版本.我更喜欢使用不同的名称,因为这样可以更容易地在编译行上为链接到库的工具选择静态与动态链接.通常我选择libMyLib.so(共享)和libMyLib_static.a(静态)之类的名称.(那些将是linux上的名字.)

  • **注意**这不是建议的方式再做它.对于非平凡大小的项目(需要几分钟而不是编译的秒数),避免将编译时间加倍是令人惊讶的.有关对象库用法或文档,请参阅下面的user465139答案:https://cmake.org/cmake/help/v3.8/command/add_library.html#object-libraries (5认同)
  • @KymikoLoco:对象库方法确实确实将编译时间减少了一半,但是它要求将静态库构建为位置独立代码(即,使用-fPIC`),当使用这些静态库时,这会增加少量的运行时开销。因此,为了获得最佳性能,此答案仍然是最好的。 (3认同)

Lar*_*dua 80

从CMake版本2.8.8开始,您可以使用"对象库" 来避免重复编译目标文件.使用Christopher Bruns的两个源文件库的示例:

# list of source files
set(libsrc source1.c source2.c)

# this is the "object library" target: compiles the sources only once
add_library(objlib OBJECT ${libsrc})

# shared libraries need PIC
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)

# shared and static libraries built from the same object files
add_library(MyLib_shared SHARED $<TARGET_OBJECTS:objlib>)
add_library(MyLib_static STATIC $<TARGET_OBJECTS:objlib>)
Run Code Online (Sandbox Code Playgroud)

来自CMake文档:

对象库编译源文件,但不将其目标文件存档或链接到库中.而是使用表单的表达式作为源创建add_library()或者add_executable()可以引用对象的其他目标 $<TARGET_OBJECTS:objlib>,其中objlib是对象库名称.

简单地说,该add_library(objlib OBJECT ${libsrc})命令指示CMake将源文件编译为*.o目标文件.*.o然后,$<TARGET_OBJECT:objlib>在两个add_library(...)命令中引用此文件集合,这两个命令调用相应的库创建命令,这些命令从同一组目标文件构建共享库和静态库.如果你有很多源文件,那么编译*.o文件可能需要很长时间; 使用对象库只需编译一次.

您支付的价格是必须将目标文件构建为与位置无关的代码,因为共享库需要这样(静态库不关心).请注意,与位置无关的代码可能效率较低,因此如果您的目标是获得最大性能,那么您将选择静态库.此外,分发静态链接的可执行文件更容易.

  • 这对我来说就像一个魅力 - 唯一的警告是随后的`target_link_libraries()`调用依赖于你的库不能使用"对象库"来链接; 那些必须以新的共享或静态库为目标(并且可能是重复的).但与第一个评论者的经验相反,这非常有用,并允许我删除所有重复的目标,并将我的所有`CMakeLists.txt`文件切成接近一半. (2认同)
  • @gnac我无法证实这一点.在我的例子中,`set_property`只在我使用`objlib`时才有用,而不是在使用`$ {objlib}`时.那么也许这个答案可以纠正? (2认同)
  • PIC 属性不应该仅应用于“MyLib_shared”吗? (2认同)

小智 23

通常无需为您的目的复制ADD_LIBRARY调用.只是利用

$> man cmake | grep -A6 '^ *BUILD_SHARED_LIBS$' 
   BUILD_SHARED_LIBS
          Global flag to cause add_library to create shared libraries if on.

          If present and true, this will cause all libraries to be built shared unless the library was
          explicitly added as a static library.  This variable is often added to projects as an OPTION
          so  that each user of a project can decide if they want to build the project using shared or
          static libraries.
Run Code Online (Sandbox Code Playgroud)

使用-DBUILD_SHARED_LIBS构建第一个(在一个源外目录中):BOOL = ON并在另一个中使用OFF

  • 这似乎没有构建BOTH静态和共享版本,我认为这是这个问题的结果. (33认同)

puc*_*chu 5

请注意,以前的答案不适用于MSVC

add_library(test SHARED ${SOURCES})
add_library(testStatic STATIC ${SOURCES})
set_target_properties(testStatic PROPERTIES OUTPUT_NAME test)
Run Code Online (Sandbox Code Playgroud)

CMake 将与目标test.dll一起创建。它将在同一目录中为目标创建并替换前一个目录。如果您尝试将某些可执行文件与目标链接,它将失败并出现如下错误:test.libtest.expsharedtest.libstaticshared

error LNK2001: unresolved external symbol __impl_*.`.
Run Code Online (Sandbox Code Playgroud)

ARCHIVE_OUTPUT_DIRECTORY为目标使用并使用一些唯一的输出目录static

add_library(test SHARED ${SOURCES})
add_library(testStatic STATIC ${SOURCES})
set_target_properties(
  testStatic PROPERTIES
  OUTPUT_NAME test
  ARCHIVE_OUTPUT_DIRECTORY testStatic
)
Run Code Online (Sandbox Code Playgroud)

test.lib将在目录中创建testStatic并且不会test.libtest目标覆盖。它与 完美配合MSVC