Cmake ile paylaşılan bir kütüphane nasıl oluşturulur?


142

Kendi yazdığım bir Makefile kullanarak derlediğim bir kütüphane yazdım, ama şimdi cmake'ye geçmek istiyorum. Ağaç şöyle görünüyor (Alakasız tüm dosyaları kaldırdım):

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

Bu yüzden yapmaya çalıştığım sadece kaynağı paylaşılan bir kütüphaneye derlemek ve daha sonra başlık dosyaları ile yüklemek.

Bulduğum çoğu örnek, bazı paylaşılan kütüphanelerle yürütülebilir dosyaları derler, ancak asla sadece düz bir paylaşılan kütüphaneyi derlemez. Birisi bana cmake kullanan çok basit bir kütüphaneyi anlatabilseydi de yararlı olurdu, bu yüzden bunu örnek olarak kullanabilirim.



Aynı soru, ama benim kaynakları karışık korumak için bir yol var mı (aynı kaynaklar dizininde .h ans .cpp) ama Cmake çalışmalarının bir dahil dizin, ürün üretmek izin vermek için?
Sandburg

Yanıtlar:


205

Her zaman için gereken minimum sürümü belirtin cmake

cmake_minimum_required(VERSION 3.9)

Bir proje ilan etmelisiniz. cmakezorunludur ve uygun değişkenleri tanımlamak söyler PROJECT_NAME, PROJECT_VERSIONve PROJECT_DESCRIPTION(3.9 cmake bu ikinci değişken gerekmek):

project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")

Yeni bir kütüphane hedefi bildirin. Lütfen kullanımından kaçının file(GLOB ...). Bu özellik, derleme sürecine katılmış ustalık sağlamaz. Tembelseniz, aşağıdakilerin kopyala-yapıştır çıktısı ls -1 sources/*.cpp:

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

Set VERSIONözelliği (isteğe bağlı, ancak iyi bir uygulamadır):

set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})

Ayrıca SOVERSIONçok sayıda olarak ayarlayabilirsiniz VERSION. Yani libmylib.so.1bir sembolik bağlantı olacak libmylib.so.1.0.0.

set_target_properties(mylib PROPERTIES SOVERSION 1)

Kütüphanenizin herkese açık API'sını bildirin. Bu API üçüncü taraf uygulaması için yüklenecek. Proje ağacınızda izole etmek iyi bir uygulamadır ( include/dizini yerleştirmek gibi ). Özel üstbilgilerin yüklenmemesi gerektiğine dikkat edin ve bunları kaynak dosyalarla birlikte yerleştirmenizi önemle öneririm.

set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)

Alt dizinlerle çalışıyorsanız, gibi göreli yollar eklemek çok uygun değildir "../include/mylib.h". Bu nedenle, dahil edilen dizinlerde bir üst dizini geçirin:

target_include_directories(mylib PRIVATE .)

veya

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

Kitaplığınız için bir yükleme kuralı oluşturun. Ben CMAKE_INSTALL_*DIRtanımlanmış değişkenleri kullanmanızı öneririz GNUInstallDirs:

include(GNUInstallDirs)

Ve yüklenecek dosyaları bildirin:

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

Ayrıca bir pkg-configdosyayı dışa aktarabilirsiniz . Bu dosya, üçüncü taraf bir uygulamanın kitaplığınızı kolayca içe aktarmasına izin verir:

Adlı bir şablon dosyası oluşturun mylib.pc.in( daha fazla bilgi için pc (5) kılavuzuna bakınız ):

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}

Gözlerinde farklı CMakeLists.txt, genişletmek için bir kural eklemek @makro ( @ONLYformun değişkenleri genişletmek değil CKağıt sormak ${VAR}):

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

Ve son olarak, oluşturulan dosyayı yükleyin:

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

Ayrıca cmake EXPORTözelliğini de kullanabilirsiniz . Ancak, bu özellik sadece uyumlu cmakeve kullanımı zor buluyorum.

Sonunda, bütün CMakeLists.txtşöyle görünmelidir:

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)

10
Sadece @ Jezz'in harika açıklamasını tamamlıyoruz: yukarıdaki tüm adımlardan sonra, programcı kütüphaneyi oluşturabilir ve kurabilir mkdir build && cd build/ && cmake .. && sudo make install(veya çizgili kütüphane sürümünü sudo make install/stripkurabilir ).
silvioprog

1
Kütüphane bağımlılıklarını aşmak için bir tekniğiniz var mı? Örneğin, mylib liblog4cxx'ye bağlıysa, bunu mylib.pc'ye akıtmanın iyi bir yolu ne olurdu?
mpr

1
liblog4cxx bir sağlarsanız @mpr .pcdosyayı eklemek Requires: liblog4cxxsenin için mylib.pc, başka, sadece ekleyebilir -llog4cxxiçin Libs:.
Jérôme Pouiller

Bu kütüphaneyi başka bir projede nasıl kullanırım? Örneğinizi uzatabilir misiniz?
Damir Porobic

Ve tüm başlıkları PUBLIC_HEADER'a mı yoksa sadece diğer kullanıcıların görebilmesi için mi eklemelisiniz? Değilse, diğer tüm başlıklarla ne yaparsınız?
Damir Porobic

84

Bu minimal CMakeLists.txtdosya basit bir paylaşılan kütüphaneyi derler:

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)

Ancak, CMake ile dosyaları farklı bir hedefe kopyalama deneyimim yok. COPY / INSTALL imzalı dosya komutu faydalı olabilir.


34
CMAKE_BUILD_TYPE atlanmalıdır, bu nedenle karar derleyene bağlıdır.
ManuelSchneid3r

Belirterek mu ${CMAKE_CURRENT_SOURCE_DIR}/içinde include_directoriesyararlıdır?
Jérôme Pouiller

@Jezz Sanmıyorum, aynı dizin önek olmadan dahil edilir. Bununla birlikte, bir alt dizinde olmanız önemli olacaktır.
Arnav Borborah

Ve kaynaklarımı ve başlıklarımı genel bir "kaynak" dizininde karıştırmak istersem ne olur? Kaynaklarımdan "başlık" dizini oluşturmak için bir "post nesil" olasılığı var mı? (belki komutları yükleyin)
Sandburg

24

Bunu kendim nasıl yapacağımı öğrenmeye çalışıyorum ve kütüphaneyi şu şekilde yükleyebilirsiniz:

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

12

İlk olarak, bu kullandığım dizin düzeni:

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

Buna bir kaç gün sonra, modern CMake sayesinde bunu yapmanın en sevdiğim yolu:

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)

CMake'i çalıştırıp kütüphaneyi kurduktan sonra Find ***. Cmake dosyalarını kullanmaya gerek yoktur, şu şekilde kullanılabilir:

find_package(MyLib REQUIRED)

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

Bu, standart bir dizine yüklenmişse bulunur ve başka bir şey yapmaya gerek yoktur. Standart olmayan bir yola kurulduysa, bu da kolaydır, CMake'e MyLibConfig.cmake dosyasını nerede bulacağını söyleyin:

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

Umarım bu bana yardım ettiği kadar herkese yardımcı olur. Bunu yapmanın eski yolları oldukça hantaldı.

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.