【发布时间】:2011-01-10 06:13:21
【问题描述】:
相同的来源,只是想要一个静态和共享版本。容易吗?
【问题讨论】:
-
这个问题的所有答案都是错误的或不完整的。我写了一个blog post about this here。感谢 PIC(除其他外),最好的办法就是创建 一个 目标并构建两次。
相同的来源,只是想要一个静态和共享版本。容易吗?
【问题讨论】:
是的,这很容易。只需使用两个“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 docs:
对象库编译源文件但不归档或链接 他们的目标文件到一个库中。而是由创建的其他目标
add_library()或add_executable()可以使用$<TARGET_OBJECTS:objlib>形式的表达式作为源,其中 objlib 是对象库名称。
简单地说,add_library(objlib OBJECT ${libsrc}) 命令指示 CMake 将源文件编译为 *.o 目标文件。这个*.o 文件集合随后在两个add_library(...) 命令中称为$<TARGET_OBJECT:objlib>,这两个命令调用适当的库创建命令,这些命令从同一组 目标文件构建共享库和静态库.如果你有很多源文件,那么编译*.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。
【讨论】:
正如前面的答案所建议的那样,可以在同一个编译过程中打包所有内容,但我建议不要这样做,因为最终它是一种仅适用于简单项目的 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 将为shared 目标创建test.dll 以及test.lib 和test.exp。然后它将在同一目录中为static 目标创建test.lib 并替换前一个。如果您尝试将某些可执行文件与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 目标覆盖test.lib。它与MSVC 完美搭配。
【讨论】:
确实有可能。正如@Christopher Bruns 在他的回答中所说,您需要添加两个版本的库:
set(libsrc source1.c source2.c source3.c)
add_library(mylib-static STATIC ${libsrc})
add_library(mylib-shared SHARED ${libsrc})
然后,如here 所述,您需要指定两个目标应使用相同的输出名称并且不覆盖彼此的文件:
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 上)。
【讨论】: