Apakah mungkin meminta CMake untuk membuat versi statis dan bersama dari pustaka yang sama?

150

Sumber yang sama, semua itu, hanya menginginkan versi statis dan berbagi keduanya. Mudah untuk dilakukan?

gct
sumber

Jawaban:

131

Ya, ini cukup mudah. Cukup gunakan dua perintah "add_library":

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

Meskipun Anda memiliki banyak file sumber, Anda akan menempatkan daftar sumber dalam variabel cmake, jadi masih mudah dilakukan.

Pada Windows Anda mungkin harus memberikan nama yang berbeda untuk setiap perpustakaan, karena ada file ".lib" untuk berbagi dan statis. Tetapi di Linux dan Mac Anda bahkan dapat memberi kedua pustaka nama yang sama (misalnya libMyLib.adan libMyLib.so):

set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)

Tapi saya tidak merekomendasikan memberikan versi statis dan dinamis dari pustaka nama yang sama. Saya lebih suka menggunakan nama yang berbeda karena itu membuatnya lebih mudah untuk memilih tautan statis vs. dinamis pada baris kompilasi untuk alat yang ditautkan ke perpustakaan. Biasanya saya memilih nama seperti libMyLib.so(shared) dan libMyLib_static.a(static). (Itu akan menjadi nama-nama di linux.)

Christopher Bruns
sumber
Tadinya berharap mereka memiliki nama yang sama, tapi oh baiklah. Pertanyaan lain: Dapatkah Anda memberi tahu CMake untuk menautkan pustaka statis ke pustaka bersama jika memungkinkan?
gct
Lebih lanjut tentang "nama yang sama": Jika Anda menggunakan Windows dan menginginkan nama yang sama untuk kedua pustaka dan Anda tidak memerlukan file .lib bersama, Anda dapat membuat .lib statis dan .dll bersama. Tetapi Anda memerlukan file .lib bersama tersebut jika Anda menggunakan library Anda untuk menghubungkan waktu kompilasi biasa.
Christopher Bruns
1
Saya tidak yakin saya mengerti pertanyaan Anda tentang menautkan perpustakaan statis ke perpustakaan bersama.
Christopher Bruns
8
Perhatikan bahwa ini bukan cara yang disarankan untuk melakukannya lagi. Untuk project berukuran non-trivially (yang memerlukan beberapa menit, bukan detik untuk dikompilasi), sangat menakjubkan menghindari penggandaan waktu kompilasi. Lihat jawaban pengguna465139 di bawah untuk penggunaan Object Library atau docs: cmake.org/cmake/help/v3.8/command/…
KymikoLoco
4
@KymikoLoco: Pendekatan Object Library memang mengurangi waktu kompilasi hingga setengahnya, tetapi membutuhkan library statis yang dibangun sebagai Position Independent Code (yaitu dengan -fPIC), yang menambahkan sejumlah kecil overhead runtime saat library statis tersebut digunakan. Jadi untuk performa maksimal, jawaban ini tetap yang terbaik.
John Zwinck
103

Sejak CMake versi 2.8.8, Anda dapat menggunakan "perpustakaan objek" untuk menghindari kompilasi file objek yang digandakan . Menggunakan contoh pustaka Christopher Bruns dengan dua file sumber:

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

Dari dokumen CMake :

Pustaka objek mengompilasi file sumber tetapi tidak mengarsipkan atau menautkan file objeknya ke pustaka. Sebaliknya, target lain yang dibuat oleh add_library()atau add_executable()mungkin mereferensikan objek menggunakan ekspresi formulir $<TARGET_OBJECTS:objlib>sebagai sumber, di mana objlib adalah nama pustaka objek.

Sederhananya, add_library(objlib OBJECT ${libsrc})perintah menginstruksikan CMake untuk mengkompilasi file sumber ke *.ofile objek. Kumpulan *.ofile ini kemudian disebut sebagai $<TARGET_OBJECT:objlib>dua add_library(...)perintah yang menjalankan perintah pembuatan perpustakaan yang sesuai yang membangun perpustakaan bersama dan statis dari kumpulan file objek yang sama . Jika Anda memiliki banyak file sumber, maka kompilasi *.ofile bisa memakan waktu cukup lama; dengan pustaka objek Anda mengkompilasinya hanya sekali.

Harga yang Anda bayarkan adalah bahwa file objek harus dibuat sebagai kode yang tidak bergantung pada posisi karena library bersama memerlukan ini (library statis tidak peduli). Perhatikan bahwa kode yang tidak tergantung posisi mungkin kurang efisien, jadi jika Anda bertujuan untuk kinerja maksimal maka Anda akan menggunakan pustaka statis. Selain itu, lebih mudah untuk mendistribusikan file executable yang terhubung secara statis.

Laring Decidua
sumber
3
Ini bekerja seperti pesona bagi saya - satu-satunya peringatan adalah target_link_libraries()panggilan berikutnya yang bergantung pada perpustakaan Anda tidak dapat menggunakan "perpustakaan objek" untuk ditautkan; mereka harus menargetkan pustaka bersama atau statis baru (dan mungkin digandakan). Tetapi bertentangan dengan pengalaman pemberi komentar pertama, ini cukup berguna, dan memungkinkan saya untuk menghapus semua target duplikat dan memotong semua CMakeLists.txtfile saya hampir setengahnya.
fish2000
1
Apakah Anda perlu "keluar" dari obblib saat menyetel properti target? yaitu set_property (TARGET $ {objlib} PROPERTI ...) vs set_property (TARGET objlib PROPERTI ...)
gnac
1
Siapa pun yang memberikan suara negatif ini ... dapatkah orang itu memberikan penjelasan yang dianggapnya tidak benar? Terlebih lagi karena ini adalah cara yang disarankan untuk melakukan apa yang diinginkan OP, lihat dokumen CMake.
Laring Decidua
1
@ user465139 Mungkin Anda harus menjelaskan mengapa harus bekerja menggunakan kembali file objek untuk target statis dan bersama. Terutama, pengetahuan umum tentang SO masih sangat membingungkan, / arsip lama juga tidak membantu untuk menjelaskannya, mis. cmake.org/pipermail/cmake/2008-March/020315.html Diperlukan penjelasan yang kuat tentang status quo. ps Bukan saya yang memberi
suara negatif
2
@gnac Saya tidak bisa mengkonfirmasi ini. Dalam kasus saya, set_propertysatu - satunya bekerja saat saya digunakan objlibdan tidak saat menggunakan ${objlib}. Jadi mungkin jawaban ini bisa diperbaiki?
josch
23

Biasanya tidak perlu menggandakan ADD_LIBRARYpanggilan untuk tujuan Anda. Manfaatkan saja

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

saat membangun, pertama (dalam satu direktori di luar sumber) dengan -DBUILD_SHARED_LIBS:BOOL=ON, dan dengan OFFdi direktori lainnya.

Yaroslav Halchenko
sumber
46
Ini tampaknya tidak membangun KEDUA versi statis dan bersama, yang menurut saya adalah tujuan dari pertanyaan ini.
Nick Desaulniers
Untuk memperjelas: Proyek ini dibangun dua kali, sekali dengan statis dan sekali dengan perpustakaan bersama. Ini solusi, jika membutuhkan pengecualian untuk kedua kasus. Tapi itu bekerja untuk semua proyek CMake tanpa adaptasi adalah cara yang paling "alami" atau "CMake".
usr1234567
0

Dimungkinkan untuk mengemas semuanya dalam nafas kompilasi yang sama, seperti yang disarankan dalam jawaban sebelumnya, tetapi saya akan menyarankan agar tidak melakukannya, karena pada akhirnya ini adalah peretasan yang hanya berfungsi untuk proyek-proyek sederhana. Misalnya, Anda mungkin memerlukan flag yang berbeda untuk versi library yang berbeda (khususnya pada Windows di mana flag biasanya digunakan untuk beralih antara mengekspor simbol atau tidak). Atau seperti yang disebutkan di atas, Anda mungkin ingin memasukkan .libfile ke direktori yang berbeda tergantung pada apakah mereka sesuai dengan pustaka statis atau bersama. Masing-masing rintangan itu membutuhkan peretasan baru.

Mungkin sudah jelas, tetapi satu alternatif yang belum disebutkan sebelumnya adalah membuat tipe pustaka sebagai parameter:

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

Memiliki versi bersama dan statis pustaka dalam dua pohon biner berbeda membuatnya lebih mudah untuk menangani opsi kompilasi yang berbeda. Saya tidak melihat kekurangan serius dalam menjaga struktur kompilasi berbeda, terutama jika kompilasi Anda dilakukan secara otomatis.

Perhatikan bahwa meskipun Anda bermaksud untuk saling menguntungkan kompilasi menggunakan OBJECTpustaka perantara (dengan peringatan yang disebutkan di atas, jadi Anda memerlukan alasan kuat untuk melakukannya), Anda masih bisa memiliki pustaka akhir yang dimasukkan ke dalam dua proyek berbeda.

P-Gn
sumber
-2

Itu memang mungkin. Seperti yang dikatakan @Christopher Bruns dalam jawabannya, Anda perlu menambahkan dua versi perpustakaan:

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

Kemudian, seperti yang dijelaskan di sini , Anda perlu menentukan bahwa kedua target harus menggunakan nama keluaran yang sama dan tidak menimpa file satu sama lain:

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)

Dengan cara ini, Anda akan mendapatkan libmylib.a dan libmylib.so (di Linux) atau mylib.lib dan mylib.dll (di Windows).

Alexander Amelkin
sumber
10
Ini tidak diperlukan saat menggunakan CMake versi di atas 2.8. [0?], Karena properti telah dihapus pada tahun 2009, dan perilaku yang diberikan sekarang menjadi default. Ini mungkin berguna untuk orang-orang di bawah 2.8, tetapi jika Anda masih menggunakan CMake <2.7, saya mohon Anda untuk meningkatkan. github.com/Kitware/CMake/commit/…
KymikoLoco