Bagaimana cara membuat perpustakaan bersama dengan cmake?

148

Saya telah menulis perpustakaan yang biasa saya kompilasi menggunakan Makefile yang ditulis sendiri, tetapi sekarang saya ingin beralih ke cmake. Pohon itu terlihat seperti ini (saya menghapus semua file yang tidak relevan):

.
├── include
│   ├── animation.h
│   ├── buffers.h
│   ├── ...
│   ├── vertex.h
│   └── world.h
└── src
    ├── animation.cpp
    ├── buffers.cpp
    ├── ...
    ├── vertex.cpp
    └── world.cpp

Jadi apa yang saya coba lakukan hanyalah mengkompilasi sumber ke perpustakaan bersama dan kemudian menginstalnya dengan file header.

Sebagian besar contoh yang saya temukan dapat dikompilasi dengan beberapa perpustakaan bersama tetapi tidak pernah hanya perpustakaan bersama biasa. Akan sangat membantu jika seseorang bisa memberi tahu saya perpustakaan yang sangat sederhana yang menggunakan cmake, jadi saya bisa menggunakan ini sebagai contoh.

Florian M
sumber
terkait stackoverflow.com/questions/2152077/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Pertanyaan yang sama, tetapi apakah ada cara untuk mempertahankan campuran sumber saya (.h ans .cpp dalam direktori sumber yang sama) tetapi membiarkan Cmake menghasilkan direktori penyertaan, produk dari pekerjaannya?
Sandburg

Jawaban:

232

Selalu tentukan versi minimum yang diwajibkan cmake

cmake_minimum_required(VERSION 3.9)

Anda harus mendeklarasikan sebuah proyek. cmakemengatakan itu wajib dan itu akan menentukan variabel yang nyaman PROJECT_NAME, PROJECT_VERSIONdan PROJECT_DESCRIPTION(variabel terakhir ini memerlukan cmake 3.9):

project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")

Deklarasikan target perpustakaan baru. Harap hindari penggunaan file(GLOB ...). Fitur ini tidak memberikan penguasaan proses kompilasi. Jika Anda malas, hasil copy-paste dari ls -1 sources/*.cpp:

add_library(mylib SHARED
    sources/animation.cpp
    sources/buffers.cpp
    [...]
)

Set VERSIONproperti (opsional tetapi ini adalah praktik yang baik):

set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})

Anda juga dapat mengatur SOVERSIONke sejumlah besar VERSION. Jadi libmylib.so.1akan menjadi symlink ke libmylib.so.1.0.0.

set_target_properties(mylib PROPERTIES SOVERSION 1)

Deklarasikan API publik perpustakaan Anda. API ini akan dipasang untuk aplikasi pihak ketiga. Ini adalah praktik yang baik untuk mengisolasinya di pohon proyek Anda (seperti menempatkan include/direktori itu ). Perhatikan bahwa, tajuk pribadi tidak boleh dipasang dan saya sangat menyarankan untuk menempatkannya dengan file sumber.

set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)

Jika Anda bekerja dengan subdirektori, sangat tidak nyaman untuk menyertakan jalur relatif seperti "../include/mylib.h". Jadi, teruskan direktori teratas di direktori yang disertakan:

target_include_directories(mylib PRIVATE .)

atau

target_include_directories(mylib PRIVATE include)
target_include_directories(mylib PRIVATE src)

Buat aturan instal untuk perpustakaan Anda. Saya menyarankan untuk menggunakan variabel yang CMAKE_INSTALL_*DIRditentukan dalam GNUInstallDirs:

include(GNUInstallDirs)

Dan nyatakan file untuk diinstal:

install(TARGETS mylib
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

Anda juga dapat mengekspor pkg-configfile. File ini memungkinkan aplikasi pihak ketiga untuk dengan mudah mengimpor perpustakaan Anda:

Buat file template bernama mylib.pc.in(lihat pc (5) halaman manual untuk informasi lebih lanjut):

prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@

Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
Version: @PROJECT_VERSION@

Requires:
Libs: -L${libdir} -lmylib
Cflags: -I${includedir}

Di Anda CMakeLists.txt, tambahkan aturan untuk memperluas @makro ( @ONLYminta cmake untuk tidak memperluas variabel formulir ${VAR}):

configure_file(mylib.pc.in mylib.pc @ONLY)

Dan terakhir, instal file yang dihasilkan:

install(FILES ${CMAKE_BINARY_DIR}/mylib.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)

Anda juga dapat menggunakan fitur cmakeEXPORT . Namun, fitur ini hanya kompatibel cmakedan menurut saya sulit untuk digunakan.

Akhirnya keseluruhannya CMakeLists.txtakan terlihat seperti:

cmake_minimum_required(VERSION 3.9)
project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")
include(GNUInstallDirs)
add_library(mylib SHARED src/mylib.c)
set_target_properties(mylib PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION 1
    PUBLIC_HEADER api/mylib.h)
configure_file(mylib.pc.in mylib.pc @ONLY)
target_include_directories(mylib PRIVATE .)
install(TARGETS mylib
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${CMAKE_BINARY_DIR}/mylib.pc
    DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
Jérôme Pouiller
sumber
12
Hanya melengkapi penjelasan mengagumkan @ Jezz: setelah semua langkah di atas, pemrogram dapat membangun dan menginstal perpustakaan dengan mkdir build && cd build/ && cmake .. && sudo make install(atau sudo make install/stripmenginstal versi perpustakaan bergaris ).
silvioprog
1
Apakah Anda memiliki teknik untuk meneruskan dependensi library? Misalnya, jika mylib bergantung pada liblog4cxx, apa cara yang baik untuk mengalirkannya ke mylib.pc?
mpr
1
@mpr Jika liblog4cxx menyediakan .pcfile, tambahkan Requires: liblog4cxxke mylib.pc, lain, Anda dapat menambahkan -llog4cxxke Libs:.
Jérôme Pouiller
Bagaimana saya menggunakan perpustakaan ini di proyek lain? Bisakah Anda memperluas teladan Anda?
Damir Porobic
Dan apakah Anda menambahkan semua tajuk ke PUBLIC_HEADER atau hanya yang dapat dilihat oleh pengguna lain? Jika tidak, apa yang Anda lakukan dengan semua tajuk lainnya?
Damir Porobic
87

CMakeLists.txtFile minimal ini mengumpulkan pustaka bersama sederhana:

cmake_minimum_required(VERSION 2.8)

project (test)
set(CMAKE_BUILD_TYPE Release)

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(test SHARED src/test.cpp)

Namun, saya tidak memiliki pengalaman menyalin file ke tujuan lain dengan CMake. Perintah file dengan tanda tangan COPY / INSTALL sepertinya berguna.

Robert Franke
sumber
36
CMAKE_BUILD_TYPE harus dihilangkan, jadi keputusan ada di tangan orang yang mengkompilasi.
ManuelSchneid3r
Apakah menentukan ${CMAKE_CURRENT_SOURCE_DIR}/dalam include_directoriesberguna?
Jérôme Pouiller
@Jezz Saya rasa tidak, direktori yang sama disertakan tanpa awalan. Namun, akan menjadi masalah jika Anda berada di subdirektori.
Arnav Borborah
Dan bagaimana jika saya ingin mencampur sumber dan tajuk saya dalam direktori "sumber" generik? Apakah ada kemungkinan "pembuatan postingan" untuk membuat direktori "header" dari sumber saya? (instal perintah mungkin)
Sandburg
24

Saya mencoba mempelajari cara melakukan ini sendiri, dan tampaknya Anda dapat menginstal library seperti ini:

cmake_minimum_required(VERSION 2.4.0)

project(mycustomlib)

# Find source files
file(GLOB SOURCES src/*.cpp)

# Include header files
include_directories(include)

# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})

# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})

# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include/${PROJECT_NAME})
birgersp.dll
sumber
13

Pertama, ini adalah tata letak direktori yang saya gunakan:

.
├── include
│   ├── class1.hpp
│   ├── ...
│   └── class2.hpp
└── src
    ├── class1.cpp
    ├── ...
    └── class2.cpp

Setelah beberapa hari melihat ini, inilah cara favorit saya untuk melakukan ini berkat CMake modern:

cmake_minimum_required(VERSION 3.5)
project(mylib VERSION 1.0.0 LANGUAGES CXX)

set(DEFAULT_BUILD_TYPE "Release")

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
  set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE)
  # Set the possible values of build type for cmake-gui
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

include(GNUInstallDirs)

set(SOURCE_FILES src/class1.cpp src/class2.cpp)

add_library(${PROJECT_NAME} ...)

target_include_directories(${PROJECT_NAME} PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
    PRIVATE src)

set_target_properties(${PROJECT_NAME} PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION 1)

install(TARGETS ${PROJECT_NAME} EXPORT MyLibConfig
    ARCHIVE  DESTINATION ${CMAKE_INSTALL_LIBDIR}
    LIBRARY  DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME  DESTINATION ${CMAKE_INSTALL_BINDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})

install(EXPORT MyLibConfig DESTINATION share/MyLib/cmake)

export(TARGETS ${PROJECT_NAME} FILE MyLibConfig.cmake)

Setelah menjalankan CMake dan menginstal pustaka, tidak perlu lagi menggunakan Find ***. File cmake, dapat digunakan seperti ini:

find_package(MyLib REQUIRED)

#No need to perform include_directories(...)
target_link_libraries(${TARGET} mylib)

Itu saja, jika sudah diinstal di direktori standar, itu akan ditemukan dan tidak perlu melakukan apa pun. Jika sudah diinstal di jalur non-standar, itu juga mudah, cukup beri tahu CMake di mana menemukan MyLibConfig.cmake menggunakan:

cmake -DMyLib_DIR=/non/standard/install/path ..

Saya harap ini membantu semua orang sebanyak itu telah membantu saya. Cara lama untuk melakukan ini cukup rumit.

Luis
sumber