Answers:
是的,这相当容易。只需使用两个“ add_library”命令:
add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)
即使您有许多源文件,也可以将源列表放置在cmake变量中,因此仍然很容易做到。
在Windows上,您可能应该给每个库一个不同的名称,因为共享和静态都有一个“ .lib”文件。但是在Linux和Mac上,您甚至可以给两个库使用相同的名称(例如libMyLib.a
和libMyLib.so
):
set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)
但是我不建议给库的静态和动态版本使用相同的名称。我更喜欢使用不同的名称,因为这样可以更轻松地在编译行上为链接到库的工具选择静态与动态链接。通常我会选择libMyLib.so
(共享)和libMyLib_static.a
(静态)之类的名称。(这些将是Linux上的名称。)
-fPIC
),这在使用这些静态库时会增加少量的运行时开销。因此,为了获得最佳性能,此答案仍然是最好的。
从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
文件可能会花费很长时间。使用对象库,您只能将它们编译一次。
您要付出的代价是必须将目标文件构建为与位置无关的代码,因为共享库需要这样做(静态库无关紧要)。请注意,与位置无关的代码效率可能较低,因此,如果要获得最佳性能,则需要静态库。此外,更容易分发静态链接的可执行文件。
target_link_libraries()
依赖于您的库的后续 调用无法使用“对象库”进行链接;这些必须以新的共享库或静态库为目标(并且可能是重复的)。但是,与第一位评论者的经验相反,这非常有用,它使我可以删除所有重复的目标并将所有CMakeLists.txt
文件削减近一半。
set_property
只有在使用时才起作用objlib
,而在使用时不起作用${objlib}
。所以也许这个答案可以纠正?
通常无需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
在其他。
正如前面的答案中所建议的那样,可以在相同的编译过程中进行打包,但是我建议不要这样做,因为最终它是一种仅适用于简单项目的技巧。例如,对于库的不同版本,有时可能需要不同的标志(特别是在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
库使编译过程相互化(有上述注意事项,因此您也有充分的理由这样做),仍然可以将最终库放在两个不同的项目中。
确实有可能。正如@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上)。