Можно ли заставить CMake создавать как статическую, так и общую версию одной и той же библиотеки?

150

Один и тот же источник, все это, просто нужна как статическая, так и общая версия. Легко сделать?

gct
источник

Ответы:

131

Да, в меру легко. Просто используйте две команды 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 по возможности связывать статические библиотеки с общей библиотекой?
gct
Подробнее об «одинаковом имени»: если вы работаете в Windows и хотите использовать одинаковое имя для обеих библиотек, и вам не нужен общий файл .lib, можно создать статический .lib и общий .dll. Но вам понадобится этот общий файл .lib, если вы используете свою библиотеку для обычной компоновки во время компиляции.
Кристофер Брунс,
1
Я не уверен, что понимаю ваш вопрос о связывании статических библиотек с общей библиотекой.
Кристофер Брунс,
8
Обратите внимание, что это больше не рекомендуемый способ. Для проектов нетривиального размера (для которых компиляция занимает минуты, а не секунды) просто замечательно избегать удвоения времени компиляции. См. Ответ user465139 ниже об использовании библиотеки объектов или документации: cmake.org/cmake/help/v3.8/command/…
KymikoLoco
4
@KymikoLoco: подход с использованием библиотеки объектов действительно сокращает время компиляции вдвое, но требует, чтобы статические библиотеки были построены как независимый от позиции код (т.е. с -fPIC), что добавляет небольшие накладные расходы времени выполнения при использовании этих статических библиотек. Так что для максимальной производительности этот ответ по-прежнему лучший.
John Zwinck
103

Начиная с версии 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()могут ссылаться на объекты, используя выражение формы $<TARGET_OBJECTS:objlib>в качестве источника, где objlib - это имя библиотеки объектов.

Проще говоря, add_library(objlib OBJECT ${libsrc})команда инструктирует CMake скомпилировать исходные файлы в *.oобъектные файлы. Этот набор *.oфайлов затем упоминается как $<TARGET_OBJECT:objlib>в двух add_library(...)командах, которые вызывают соответствующие команды создания библиотеки, которые строят общие и статические библиотеки из одного и того же набора объектных файлов. Если у вас много исходных файлов, то компиляция *.oфайлов может занять довольно много времени; с библиотеками объектов вы компилируете их только один раз.

Цена, которую вы платите, состоит в том, что объектные файлы должны быть построены как позиционно-независимый код, потому что это необходимо разделяемым библиотекам (статические библиотеки не заботятся). Обратите внимание, что позиционно-независимый код может быть менее эффективным, поэтому, если вы стремитесь к максимальной производительности, вы должны использовать статические библиотеки. Кроме того, легче распространять статически связанные исполняемые файлы.

Гортань децидуальная
источник
3
Для меня это сработало как шарм - единственное предостережение - последующие target_link_libraries()вызовы, которые зависят от вашей библиотеки, не могут использовать «библиотеку объектов» для компоновки; они должны быть нацелены на новые общие или статические библиотеки (и могут дублироваться). Но вопреки опыту первых комментаторов это было весьма полезно и позволило мне удалить все повторяющиеся цели и сократить все мои CMakeLists.txtфайлы почти наполовину.
fish2000
1
Вам нужно "экранировать" облиб при установке целевых свойств? т.е. set_property (TARGET $ {objlib} PROPERTY ...) vs set_property (TARGET objlib PROPERTY ...)
gnac 08
1
Кто бы ни проголосовал против этого ... может ли этот человек объяснить, что он / она считает неправильным? Тем более, что это рекомендуемый способ делать то, что хочет OP, см. Документацию CMake.
Laryx Decidua
1
@ user465139 Возможно, вам стоит объяснить, почему должно работать повторное использование объектных файлов как для статической, так и для общей цели. В частности, общие знания в SO все еще очень сбивают с толку, старые / архивы также не помогают прояснить это, например. cmake.org/pipermail/cmake/2008-March/020315.html Требуется твердое объяснение статус-кво. ps Это не я проголосовал против
mloskot
2
@gnac Я не могу это подтвердить. В моем случае set_propertyсработало только при использовании, objlibа не при использовании ${objlib}. Так, может быть, этот ответ можно исправить?
josch
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в другом.

Ярослав Гальченко
источник
46
Похоже, что это не создает ОБЕИ статические и общие версии, и я думаю, что это то, к чему этот вопрос.
Nick Desaulniers
Чтобы уточнить: проект создается дважды: один раз со статическими и один раз с разделяемыми библиотеками. Это решение, если оба случая являются исключением. Но он работает для всех проектов CMake без адаптации - наиболее «естественным» или «CMake» способом.
usr1234567
0

Можно упаковать все в одном дыхании компиляции, как предлагалось в предыдущих ответах, но я бы не советовал этого, потому что, в конце концов, это хак, который работает только для простых проектов. Например, в какой-то момент вам могут понадобиться разные флаги для разных версий библиотеки (особенно в 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библиотеки (с оговорками, упомянутыми выше, поэтому вам нужна веская причина для этого), вы все равно можете поместить конечные библиотеки в два разных проекта.

P-Gn
источник
-2

Это действительно возможно. Как сказал @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).

Александр Амелькин
источник
10
В этом нет необходимости при использовании версий CMake выше 2.8. [0?], Поскольку это свойство было удалено в 2009 году, и теперь его поведение является значением по умолчанию. Это может быть полезно для людей ниже 2,8, но если вы все еще используете CMake <2,7, я умоляю вас выполнить обновление. github.com/Kitware/CMake/commit/…
KymikoLoco 05