Чи можливо змусити CMake створити як статичну, так і спільну версію однієї бібліотеки?


141

Це ж джерело, все, що потрібно, і статична, і спільна версія обох. Легко зробити?

Відповіді:


123

Так, це помірно легко. Просто використовуйте дві команди "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
Я не впевнений, що я розумію ваше запитання про приєднання статичних бібліотек до спільної бібліотеки.
Крістофер Брунс

5
Зауважте, що це вже не запропонований спосіб зробити це. Для проектів нетривіального розміру (тих, на які потрібно складати хвилини, а не секунди), уникати подвоєння часу компіляції є дивовижним. Дивіться user465139 відповідь нижче щодо використання Бібліотеки об’єктів або документів: cmake.org/cmake/help/v3.8/command/…
KymikoLoco

3
@KymikoLoco: Підхід до бібліотеки об'єктів дійсно скорочує час компіляції вдвічі, але вимагає побудови статичних бібліотек як коду незалежного від позиції (тобто з -fPIC), що додає невелику кількість накладних витрат, коли використовуються ці статичні бібліотеки. Тож для максимальної продуктивності ця відповідь все ж найкраща.
Джон Цвінк

95

Оскільки 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} ВЛАСНІСТЬ ...) vs set_property (TARGET objlib PROPERTY ...)
gnac

1
Хто б не порушив це ... чи міг би цей чоловік дати пояснення, що він / він вважав неправильним? Тим більше, що це рекомендований спосіб робити те, що хоче ОП, дивіться документи CMake.
Laryx Decidua

1
@ user465139 Можливо, вам слід пояснити, чому він повинен працювати для повторного використання об'єктних файлів як для статичної, так і для спільної цілі. Тим більше, загальні знання в ЗУ все ще дуже заплутані, але старі / архіви також не допомагають уточнити це, наприклад. cmake.org/pipermail/cmake/2008-March/020315.html Потрібне ґрунтовне пояснення статусу кво. ps Не я згадав мене
mloskot

2
@gnac Я не можу цього підтвердити. У моєму випадку працює set_propertyлише тоді, коли я користувався, objlibа не під час використання ${objlib}. То, може, цю відповідь можна було б виправити?
Джош

22

Зазвичай не потрібно дублювати 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в інший.


43
Це, здається, не створює БОТУ статичних та спільних версій, що, на мою думку, саме це питання.
Нік Десольньє

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


-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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.