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


141

相同的来源,所有这些都只需要静态版本和共享版本。容易吗?

Answers:


123

是的,这相当容易。只需使用两个“ add_library”命令:

add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)

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

在Windows上,您可能应该给每个库一个不同的名称,因为共享和静态都有一个“ .lib”文件。但是在Linux和Mac上,您甚至可以给两个库使用相同的名称(例如libMyLib.alibMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

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


希望他们有相同的名字,但是哦。另一个问题:您能否告诉CMake在可能的情况下将静态库链接到共享库?
gct

有关“相同名称”的更多信息:如果您在Windows上,并且希望两个库都使用相同的名称,并且不需要共享的.lib文件,则可以创建静态.lib和共享的.dll。但是,如果您使用库进行普通的编译时链接,则需要该共享的.lib文件。
Christopher Bruns

1
我不确定我是否理解您有关将静态库链接到共享库的问题。
Christopher Bruns

5
请注意,这不再是建议的方法。对于非平凡的项目(需要几分钟而不是几秒钟进行编译的项目),避免将编译时间加倍是不可思议的。见user465139回答下面的对象库使用或文档:cmake.org/cmake/help/v3.8/command/...
KymikoLoco

3
@KymikoLoco:对象库方法确实确实将编译时间减少了一半,但它要求将静态库构建为位置无关代码(即带有-fPIC),这在使用这些静态库时会增加少量的运行时开销。因此,为了获得最佳性能,此答案仍然是最好的。
John Zwinck

95

从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>)

CMake文档

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

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

您要付出的代价是必须将目标文件构建为与位置无关的代码,因为共享库需要这样做(静态库无关紧要)。请注意,与位置无关的代码效率可能较低,因此,如果要获得最佳性能,则需要静态库。此外,更容易分发静态链接的可执行文件。


3
这对我来说就像是一种魅力–唯一的警告是target_link_libraries()依赖于您的库的后续 调用无法使用“对象库”进行链接;这些必须以新的共享库或静态库为目标(并且可能是重复的)。但是,与第一位评论者的经验相反,这非常有用,它使我可以删除所有重复的目标并将所有CMakeLists.txt文件削减近一半。
fish2000

1
设置目标属性时是否需要“转义” obblib?即set_property(TARGET $ {objlib} PROPERTY ...)vs set_property(TARGET objlib PROPERTY ...)
gnac

1
谁对此表示反对...该人可以提供他/她认为不正确的解释吗?更是如此,因为这是OP想要做的推荐方式,请参阅CMake文档。
Laryx Decidua

1
@ user465139也许您应该解释为什么它应该可以为静态目标和共享目标重新使用目标文件。特别是,SO方面的常识仍然对此很困惑,例如,旧的/归档文件也无助于澄清。cmake.org/pipermail/cmake/2008-March/020315.html 需要对现状进行扎实的解释。ps
拒绝投票的

2
@gnac我无法确认。就我而言,set_property只有在使用时才起作用objlib,而在使用时不起作用${objlib}。所以也许这个答案可以纠正?
josch

22

通常无需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.

同时建立,首先(在一个外的源目录)与-DBUILD_SHARED_LIBS:BOOL=ON,并与OFF在其他。


43
这似乎并没有建立静态版本和共享版本,我认为这是这个问题的根源。
Nick Desaulniers 2015年

0

正如前面的答案中所建议的那样,可以在相同的编译过程中进行打包,但是我建议不要这样做,因为最终它是一种仅适用于简单项目的技巧。例如,对于库的不同版本,有时可能需要不同的标志(特别是在Windows上,这些标志通常用于在导出符号与否之间进行切换)。或如上所述,您可能希望将.lib文件放入不同的目录中,具体取决于它们是对应于静态库还是共享库。这些障碍中的每一个都将需要新的技巧。

显而易见,但是前面未提到的一种替代方法是使库的类型成为参数:

set( ${PROJECT_NAME}_LIBTYPE CACHE STRING "library type" )
set_property( CACHE ${PROJECT_NAME}_LIBTYPE PROPERTY STRINGS "SHARED;STATIC" )
add_library( ${PROJECT_NAME} ${PROJECT_NAME}_LIBTYPE ${SOURCE_FILES} )

在两个不同的二进制树中共享和静态版本的库使处理不同的编译选项更加容易。我看不出保持编译树不同的任何严重缺点,特别是如果您的编译是自动化的。

请注意,即使您打算使用中间OBJECT库使编译过程相互化(有上述注意事项,因此您也有充分的理由这样做),仍然可以将最终库放在两个不同的项目中。


-2

确实有可能。正如@Christopher Bruns在回答中所说,您需要添加两个版本的库:

set(libsrc source1.c source2.c source3.c)
add_library(mylib-static STATIC ${libsrc})
add_library(mylib-shared SHARED ${libsrc})

然后,描述在这里,你需要指定两个目标应该使用相同的输出名称,而不是相互覆盖的文件:

SET_TARGET_PROPERTIES(mylib-static PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(mylib-shared PROPERTIES OUTPUT_NAME mylib CLEAN_DIRECT_OUTPUT 1)

这样,您将同时获得libmylib.a和libmylib.so(在Linux上)或mylib.lib和mylib.dll(在Windows上)。


10
当使用高于2.8。[0?]的CMake版本时,这是不必要的,因为该属性已于2009年删除,现在提供的行为是默认行为。这对于2.8以下的用户可能有用,但是如果您仍在使用CMake <2.7,我恳请您进行升级。github.com/Kitware/CMake/commit/...
KymikoLoco
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.