Можно ли заставить CMake собрать как статическую, так и совместно используемую версию одной и той же библиотеки?
Все тот же источник, просто хочу статическую и совместно используемую версию. Легко сделать?
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 (shared) и libMyLib_static.a (static). (Это будут имена в Linux.)
Начиная с версии CMake 2.8.8, вы можете использовать "библиотеки объектов", чтобы избежать дублирования компиляции объектных файлов. Используя пример библиотеки Кристофера Брунса с двумя исходными файлами:
# 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(), могут ссылаться на объекты, используя выражение формы $ в качестве источника, где 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 в другом
Имейте в виду, что предыдущие ответы не будут работать с:
add_library(test SHARED ${SOURCES})
add_library(testStatic STATIC ${SOURCES})
set_target_properties(testStatic PROPERTIES OUTPUT_NAME test)
CMake создаст
test.dll
вместе с и
test.exp
для цели. Затем он создаст в том же каталоге для цели и заменит предыдущий. Если вы попытаетесь связать какой-нибудь исполняемый файл с
shared
target он выйдет из строя с ошибкой, например:
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
)
будет создан в
testStatic
каталог и не будет отменять
test.lib
из
test
цель. Он отлично работает с
MSVC
.
Можно упаковать все в одном дыхании компиляции, как предлагалось в предыдущих ответах, но я бы не советовал этого, потому что, в конце концов, это хак, который работает только для простых проектов. Например, в какой-то момент вам могут понадобиться разные флаги для разных версий библиотеки (особенно в 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).