Is it possible to get CMake to build both a static and shared library at the same time?

StaticCmakeShared

Static Problem Overview


Same source, all that, just want a static and shared version both. Easy to do?

Static Solutions


Solution 1 - Static

Yes, it's moderately easy. Just use two "add_library" commands:

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

Even if you have many source files, you can place the list of sources in a Cmake variable, so it's still easy to do.

On Windows you should probably give each library a different name, since there is a ".lib" file for both shared and static. But on Linux and Mac you can even give both libraries the same name (e.g. libMyLib.a and libMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

But I don't recommend giving both the static and dynamic versions of the library the same name. I prefer to use different names because that makes it easier to choose static vs. dynamic linkage on the compile line for tools that link to the library. Usually I choose names like libMyLib.so (shared) and libMyLib_static.a (static). (Those would be the names on linux.)

Solution 2 - Static

Since CMake version 2.8.8, you can use "object libraries" to avoid the duplicated compilation of the object files. Using Christopher Bruns' example of a library with two source files:

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

From the CMake docs:

> An object library compiles source files but does not archive or link > their object files into a library. Instead other targets created by > add_library() or add_executable() may reference the objects using an > expression of the form $<TARGET_OBJECTS:objlib> as a source, where > objlib is the object library name.

Simply put, the add_library(objlib OBJECT ${libsrc}) command instructs CMake to compile the source files to *.o object files. This collection of *.o files is then referred to as $<TARGET_OBJECT:objlib> in the two add_library(...) commands that invoke the appropriate library creation commands that build the shared and static libraries from the same set of object files. If you have lots of source files, then compiling the *.o files can take quite long; with object libraries you compile them only once.

The price you pay is that the object files must be built as position-independent code because shared libraries need this (static libs don't care). Note that position-independent code may be less efficient, so if you aim for maximal performance then you'd go for static libraries. Furthermore, it is easier to distribute statically linked executables.

Solution 3 - Static

There is generally no need to duplicate ADD_LIBRARY calls for your purpose. Just make use of

$> 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.

while building, first (in one out-of-source directory) with -DBUILD_SHARED_LIBS:BOOL=ON, and with OFF in the other.

Solution 4 - Static

Please be aware that previous answers won't work with MSVC:

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

CMake will create test.dll together with test.lib and test.exp for shared target. Than it will create test.lib in the same directory for static target and replace previous one. If you will try to link some executable with shared target it will fail with error like:

error LNK2001: unresolved external symbol __impl_*.`.

Please use ARCHIVE_OUTPUT_DIRECTORY and use some unique output directory for static target:

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

test.lib will be created in testStatic directory and won't override test.lib from test target. It works perfect with MSVC.

Solution 5 - Static

It's possible to pack eveything in the same compilation breath, as suggested in the previous answers, but I would advise against it, because in the end it's a hack that works only for simple projects. For example, you may need at some point different flags for different versions of the library (esp. on Windows where flags are typically used to switch between exporting symbols or not). Or as mentionned above, you may want to put .lib files into different directories depending on whether they correspond to static or shared libraries. Each of those hurdles will require a new hack.

It may be obvious, but one alternative that has not been mentionned previously is to make the type of the library a parameter:

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

Having shared and static versions of the library in two different binary trees makes it easier to handle different compilation options. I don't see any serious drawback in keeping compilation trees distinct, especially if your compilations are automated.

Note that even if you intend to mutualize compilations using an intermediate OBJECT library (with the caveats mentionned above, so you need a compelling reason to do so), you could still have end libraries put in two different projects.

Solution 6 - Static

It is indeed possible. As @Christopher Bruns said in his answer, you need to add two versions of the library:

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

Then, as described here, you need to specify that both targets should use the same output name and not overwrite each other's files:

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)

This way, you will get both libmylib.a and libmylib.so (on Linux) or mylib.lib and mylib.dll (on Windows).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongctView Question on Stackoverflow
Solution 1 - StaticChristopher BrunsView Answer on Stackoverflow
Solution 2 - StaticLaryx DeciduaView Answer on Stackoverflow
Solution 3 - StaticYaroslav HalchenkoView Answer on Stackoverflow
Solution 4 - StaticpuchuView Answer on Stackoverflow
Solution 5 - StaticP-GnView Answer on Stackoverflow
Solution 6 - StaticAlexander AmelkinView Answer on Stackoverflow