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

时间:2010-01-28 03:20:24

标签: static cmake shared

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

6 个答案:

答案 0 :(得分:110)

是的,这很容易。只需使用两个“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上的名字。)

答案 1 :(得分:71)

从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()可以使用a引用对象   表达式$<TARGET_OBJECTS:objlib>作为源,其中   objlib是对象库名称。

简单地说,add_library(objlib OBJECT ${libsrc})命令指示CMake将源文件编译为*.o目标文件。然后,这两个*.o文件集合在$<TARGET_OBJECT:objlib>命令中称为add_library(...),这两个*.o命令调用相应的库创建命令,这些命令从同一组<构建共享库和静态库< / em>目标文件。如果你有很多源文件,那么编译import java.util.*; public class demo { public static void main(String r[]) { Scanner sc = new Scanner(System.in); System.out.println("Hello ..."); System.out.println("please enter the number :"); int i = sc.nextInt(); System.out.println(" number : " + i); } } 文件需要很长时间;使用对象库只需编译一次。

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

答案 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.

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

答案 3 :(得分:0)

可以像以前的答案中一样在相同的编译过程中进行打包,但是我建议不要这样做,因为最终它是一种仅适用于简单项目的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库使编译过程相互化(带有上述注意事项,因此您也有充分的理由这样做),仍然可以将最终库放在两个不同的项目中

答案 4 :(得分:0)

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

add_library(test SHARED ${SOURCES})
add_library(testStatic STATIC ${SOURCES})
set_target_properties(testStatic PROPERTIES OUTPUT_NAME test)

CMake 将为 test.dll 目标与 test.libtest.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 完美配合。

答案 5 :(得分:-2)

确实有可能。正如@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上)。