Відповіді:
Так, це помірно легко. Просто використовуйте дві команди "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.)
-fPIC
), що додає невелику кількість накладних витрат, коли використовуються ці статичні бібліотеки. Тож для максимальної продуктивності ця відповідь все ж найкраща.
Оскільки 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
файлів може зайняти досить довго; з бібліотеками об'єктів ви збираєте їх лише один раз.
Ціна, яку ви платите, полягає в тому, що об’єктні файли повинні бути побудовані як незалежний від позиції код, оскільки для цього потрібні спільні бібліотеки (статичні лібри не хвилюються). Зауважте, що незалежний від позиції код може бути менш ефективним, тому, якщо ви прагнете до максимальної продуктивності, тоді ви будете шукати статичні бібліотеки. Крім того, легше поширювати статично пов'язані виконавчі файли.
target_link_libraries()
дзвінки, які залежать від вашої бібліотеки, не можуть використовувати «бібліотеку об’єктів» для зв’язку; вони повинні орієнтуватися на нові спільні або статичні бібліотеки (і можуть бути дублюються). Але, всупереч досвіду перших коментаторів, це було досить корисно, і дозволило мені видалити всі дублювані цілі та скоротити всі мої CMakeLists.txt
файли майже до половини.
set_property
лише тоді, коли я користувався, objlib
а не під час використання ${objlib}
. То, може, цю відповідь можна було б виправити?
Зазвичай не потрібно дублювати 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
в інший.
Можна упакувати все на тому самому диханні компіляції, як це було запропоновано в попередніх відповідях, але я б радив цього, оскільки врешті-решт це хак, який працює лише для простих проектів. Наприклад, вам можуть знадобитися в якийсь момент різні прапори для різних версій бібліотеки (особливо в 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).