相同的来源,所有这些,只想要一个静态和共享版本。容易做到吗?
6 回答
是的,这很容易。只需使用两个“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 上的名称。)
从 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
您付出的代价是目标文件必须构建为与位置无关的代码,因为共享库需要这个(静态库不在乎)。请注意,与位置无关的代码可能效率较低,因此如果您的目标是获得最大性能,那么您会选择静态库。此外,更容易分发静态链接的可执行文件。
通常不需要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
在另一个中使用。
正如前面的答案中所建议的那样,可以在同一个编译过程中打包所有内容,但我建议不要这样做,因为最终它是一种仅适用于简单项目的 hack。例如,您可能在某些时候需要为不同版本的库使用不同的标志(尤其是在 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
库来相互编译编译(上面提到的警告,所以您需要一个令人信服的理由这样做),您仍然可以将最终库放在两个不同的项目中。
请注意,以前的答案不适用于MSVC
:
add_library(test SHARED ${SOURCES})
add_library(testStatic STATIC ${SOURCES})
set_target_properties(testStatic PROPERTIES OUTPUT_NAME test)
CMake 将与目标test.dll
一起创建test.lib
并test.exp
为shared
目标创建。它将test.lib
在同一目录中为static
目标创建并替换前一个。如果您尝试将某些可执行文件与shared
目标链接,它将失败并出现如下错误:
error LNK2001: unresolved external symbol __impl_*.`.
请使用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
)
test.lib
将在testStatic
目录中创建,并且不会test.lib
从test
目标覆盖。它与MSVC
.
这确实是可能的。正如@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 上)。