CMake'in aynı kütüphanenin hem statik hem de paylaşılan bir sürümünü oluşturmasını sağlamak mümkün mü?


141

Aynı kaynak, hepsi, sadece statik ve paylaşılan bir sürüm istiyorum. Yapması kolay?

Yanıtlar:


123

Evet, orta derecede kolay. Sadece iki "add_library" komutu kullanın:

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

Çok sayıda kaynak dosyanız olsa bile, kaynak listesini bir cmake değişkenine yerleştirirsiniz, bu yüzden yine de yapmak kolaydır.

Hem paylaşılan hem de statik için bir ".lib" dosyası olduğundan, Windows'ta muhtemelen her kitaplığa farklı bir ad vermelisiniz. Ancak Linux ve Mac'te her iki kütüphaneye de aynı adı verebilirsiniz (ör. libMyLib.aVe libMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

Ancak kütüphanenin statik ve dinamik sürümlerinin aynı adı vermesini önermiyorum. Farklı isimler kullanmayı tercih ediyorum çünkü bu, kütüphaneye bağlanan araçlar için derleme satırındaki statik ve dinamik bağlantıyı seçmeyi kolaylaştırıyor. Genellikle libMyLib.so(paylaşılan) ve libMyLib_static.a(statik) gibi isimler seçerim . (Bunlar linux üzerindeki isimler olurdu.)


Aynı ada sahip olmalarını umuyordum, ama iyi. Başka bir soru: CMake'e statik kütüphaneleri mümkünse paylaşılan kütüphaneye bağlamasını söyleyebilir misiniz?
gct

"Aynı ad" hakkında daha fazla bilgi: Windows'daysanız ve her iki kitaplık için de aynı adı istiyorsanız ve paylaşılan .lib dosyasına ihtiyacınız yoksa, statik bir .lib ve paylaşılan bir .dll oluşturmak mümkündür. Ancak, sıradan derleme zamanı bağlantısı için kitaplığınızı kullanıyorsanız, bu paylaşılan .lib dosyasına ihtiyacınız vardır.
Christopher Bruns

1
Statik kitaplıkları paylaşılan kitaplığa bağlama ile ilgili sorunuzu anladığımdan emin değilim.
Christopher Bruns

5
Bunun artık yapılması için önerilen yol olmadığını unutmayın . Önemsiz boyutlarda olan projeler için (derleme dakika değil, saniyeler değil), derleme süresinin iki katına çıkmasından kaçınmak harikadır. Nesne Kitaplığı kullanımı veya dokümanlar için aşağıdaki user465139 yanıtına bakın: cmake.org/cmake/help/v3.8/command/…
KymikoLoco

3
@KymikoLoco: Nesne Kitaplığı yaklaşımı gerçekten derleme süresini yarı yarıya azaltır, ancak statik kitaplıkların Konum Bağımsız Kod (yani ile -fPIC) olarak oluşturulmasını gerektirir, bu da bu statik kitaplıklar kullanıldığında az miktarda çalışma zamanı ek yükü ekler. Maksimum performans için bu cevap hala en iyisidir.
John Zwinck

95

CMake 2.8.8 sürümünden bu yana , nesne dosyalarının yinelenen derlemesini önlemek için "nesne kitaplıkları" kullanabilirsiniz . Christopher Bruns'in iki kaynak dosyası olan bir kütüphane örneğini kullanma:

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

Gönderen CKağıt docs :

Nesne kitaplığı kaynak dosyaları derler, ancak nesne dosyalarını arşivlemez veya kitaplığa bağlamaz. Bunun yerine , formun bir ifadesini kaynak olarak kullanarak nesneler tarafından oluşturulan add_library()veya add_executable()nesnelere $<TARGET_OBJECTS:objlib>başvurabilir; buradaki objlib, nesne kitaplığı adıdır.

Basitçe söylemek gerekirse, add_library(objlib OBJECT ${libsrc})komut CMake'e kaynak dosyalarını *.onesne dosyalarına derlemesini bildirir . Bu *.odosya koleksiyonuna daha sonra aynı nesne kümesi kümesinden paylaşılan ve statik kitaplıkları oluşturan uygun kitaplık oluşturma komutlarını çağıran $<TARGET_OBJECT:objlib>iki add_library(...)komutta bahsedilir . Çok sayıda kaynak dosyanız varsa, dosyaları derlemek oldukça uzun sürebilir; nesne kitaplıklarıyla bunları yalnızca bir kez derlersiniz.*.o

Ödediğiniz fiyat, paylaşılan kitaplıkların buna ihtiyacı olduğu için statik dosyaların konumdan bağımsız bir kod olarak oluşturulması gerektiğidir (statik kütüphaneler umursamaz). Konumdan bağımsız kodun daha az verimli olabileceğine dikkat edin, bu nedenle maksimum performans hedefliyorsanız statik kitaplıkları tercih edersiniz. Ayrıca, statik olarak bağlı yürütülebilir dosyaları dağıtmak daha kolaydır.


3
Bu benim için bir cazibe gibi çalıştı - tek uyarı target_link_libraries()kitaplığınıza bağlı sonraki çağrılar bağlanmak için "nesne kitaplığı" kullanamazsınız; bunlar yeni paylaşılan veya statik kitaplıkları hedeflemelidir (ve çoğaltılabilir). Ancak ilk yorumcuların deneyiminin aksine, bu oldukça faydalı oldu ve tüm yinelenen hedefleri kaldırmamı ve tüm CMakeLists.txtdosyalarımı yarıya yakın kesmemi sağladı.
fish2000

1
Hedef özellikleri ayarlarken obblib'den "kaçmanız" gerekiyor mu? yani set_property (TARGET $ {objlib} PROPERTY ...) ile set_property (TARGET objlib PROPERTY ...) karşılaştırması
gnac

1
Bunu kim düşürdü ... o kişi neyi yanlış olarak değerlendirdiğini açıklayabilir mi? Dahası, OP'nin istediği şeyi yapmanın önerilen yolu bu olduğundan , CMake belgelerine bakın.
Laryx Decidua

1
@ user465139 Belki de hem statik hem de paylaşılan hedef için nesne dosyalarını yeniden kullanmanın neden çalışması gerektiğini açıklamalısınız. Özellikle, SO'daki genel bilgi hala bu konuda çok kafa karıştırıcıdır, eski / arşivler de bunu açıklığa kavuşturmaya yardımcı olmaz, ör. cmake.org/pipermail/cmake/2008-March/020315.html Statükonun sağlam bir açıklaması gerekiyor. ps Ben indirilen ben değildi
mloskot 27:17

2
@gnac Bunu onaylayamıyorum. Benim durumumda, set_propertysadece kullandığımda çalıştım objlibve kullanırken değil ${objlib}. Yani bu cevap düzeltilebilir mi?
josch

22

Genel olarak ADD_LIBRARY, amaçlarınız için çağrıları çoğaltmanıza gerek yoktur . Sadece faydalanın

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

yaparken, önce (kaynak dışı bir dizinde) -DBUILD_SHARED_LIBS:BOOL=ONve OFFdiğerinde ile birlikte .


43
Bu her iki statik ve paylaşılan sürümleri oluşturmak gibi görünmüyor, bu sorunun ne olduğunu düşünüyorum.
Nick Desaulniers

0

Önceki cevaplarda önerildiği gibi, her şeyi aynı derleme nefesinde paketlemek mümkündür, ama buna karşı tavsiye ederim, çünkü sonunda sadece basit projeler için çalışan bir kesmek. Örneğin, bir noktada kütüphanenin farklı sürümleri için farklı bayraklara ihtiyacınız olabilir (özellikle bayrakların dışa aktarma sembolleri arasında geçiş yapmak için kullanılıp kullanılmadığı Windows'da). Veya yukarıda belirtildiği gibi, .libstatik veya paylaşılan kitaplıklara karşılık gelmelerine bağlı olarak dosyaları farklı dizinlere koymak isteyebilirsiniz . Bu engellerin her biri için yeni bir saldırı gerekir.

Açık olabilir, ancak daha önce bahsedilmeyen bir alternatif, kütüphane türünü bir parametre yapmaktır:

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

Kütüphanenin iki farklı ikili ağaçta paylaşılan ve statik sürümleri olması, farklı derleme seçeneklerinin kullanılmasını kolaylaştırır. Özellikle derlemeleriniz otomatik ise, derleme ağaçlarını ayrı tutmak konusunda ciddi bir dezavantaj görmüyorum.

Bir ara OBJECTkitaplık (yukarıda belirtilen uyarılarla birlikte) derlemeleri karşılıklılaştırmayı planlasanız bile, bunu yapmak için zorlayıcı bir nedene ihtiyacınız var), yine de iki farklı projede son kitaplıkların olabileceğini unutmayın.


-2

Gerçekten mümkün. @Christopher Bruns'ın cevabında söylediği gibi, kütüphanenin iki versiyonunu eklemeniz gerekir:

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

Daha sonra, burada açıklandığı gibi , her iki hedefin de aynı çıktı adını kullanması ve birbirlerinin dosyalarının üzerine yazmaması gerektiğini belirtmeniz gerekir:

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)

Bu şekilde, hem libmylib.a hem de libmylib.so (Linux'ta) veya mylib.lib ve mylib.dll (Windows'ta) alırsınız.


10
Özellik 2009'da kaldırıldığı ve sağladığı davranış artık varsayılan olduğu için 2.8. [0?] Üzerindeki CMake sürümleri kullanıldığında bu gereksizdir. Bu, 2.8'in altındaki kişiler için yararlı olabilir, ancak hala CMake <2.7 kullanıyorsanız, yükseltmenizi öneririm. github.com/Kitware/CMake/commit/…
KymikoLoco
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.