จะสร้างไลบรารีที่ใช้ร่วมกันด้วย cmake ได้อย่างไร?


148

ฉันได้เขียนไลบรารีที่ฉันเคยรวบรวมโดยใช้ Makefile ที่เขียนเอง แต่ตอนนี้ฉันต้องการเปลี่ยนไปใช้ cmake ต้นไม้มีลักษณะดังนี้ (ฉันลบไฟล์ที่ไม่เกี่ยวข้องทั้งหมดออกไป):

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

ดังนั้นสิ่งที่ฉันพยายามทำก็แค่รวบรวมซอร์สลงในไลบรารีที่ใช้ร่วมกันแล้วติดตั้งด้วยไฟล์ส่วนหัว

ตัวอย่างส่วนใหญ่ที่ฉันพบว่าคอมไพล์ไฟล์ปฏิบัติการกับไลบรารีที่แชร์บางไลบรารี แต่ไม่ใช่แค่ไลบรารีที่แชร์ธรรมดา นอกจากนี้ยังจะมีประโยชน์ถ้ามีคนบอกฉันเกี่ยวกับไลบรารีธรรมดา ๆ ที่ใช้ cmake ดังนั้นฉันสามารถใช้สิ่งนี้เป็นตัวอย่างได้



คำถามเดียวกัน แต่มีวิธีการรักษาแหล่งที่มาของฉันแบบผสม (.h ans .cpp ในไดเร็กทอรีต้นทางเดียวกัน) แต่เพื่อให้ Cmake สร้างไดเร็กทอรีรวมผลิตภัณฑ์ของงาน
Sandburg

คำตอบ:


232

ระบุเวอร์ชันที่ต้องการขั้นต่ำของ cmake

cmake_minimum_required(VERSION 3.9)

คุณควรประกาศโครงการ cmakeบอกว่ามันมีผลบังคับใช้และจะกำหนดตัวแปรสะดวกPROJECT_NAME, PROJECT_VERSIONและPROJECT_DESCRIPTION(นี่เลี่ยงตัวแปรหลัง CMake 3.9):

project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")

ประกาศเป้าหมายไลบรารีใหม่ โปรดหลีกเลี่ยงการใช้file(GLOB ...). คุณลักษณะนี้ไม่ได้ให้ความเชี่ยวชาญในกระบวนการคอมไพล์ หากคุณขี้เกียจคัดลอกและวางผลลัพธ์ของls -1 sources/*.cpp:

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

ตั้งค่าVERSIONคุณสมบัติ (เป็นทางเลือก แต่เป็นแนวทางปฏิบัติที่ดี):

set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})

นอกจากนี้คุณยังสามารถตั้งค่าไปยังหมายเลขที่สำคัญของSOVERSION VERSIONดังนั้นlibmylib.so.1จะ symlink libmylib.so.1.0.0ไป

set_target_properties(mylib PROPERTIES SOVERSION 1)

ประกาศ API สาธารณะของห้องสมุดของคุณ API นี้จะถูกติดตั้งสำหรับแอปพลิเคชันของบุคคลที่สาม เป็นแนวทางปฏิบัติที่ดีในการแยกไว้ในแผนผังโครงการของคุณ (เช่นการวางinclude/ไดเรกทอรี) โปรดสังเกตว่าไม่ควรติดตั้งส่วนหัวส่วนตัวและฉันขอแนะนำอย่างยิ่งให้วางไว้กับไฟล์ต้นฉบับ

set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)

หากคุณทำงานกับไดเร็กทอรีย่อยการรวมพา ธ สัมพัทธ์เช่น"../include/mylib.h". ดังนั้นส่งไดเรกทอรีชั้นนำในไดเรกทอรีที่รวมไว้:

target_include_directories(mylib PRIVATE .)

หรือ

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

สร้างกฎการติดตั้งสำหรับไลบรารีของคุณ ฉันขอแนะนำให้ใช้ตัวแปรที่CMAKE_INSTALL_*DIRกำหนดในGNUInstallDirs:

include(GNUInstallDirs)

และประกาศไฟล์ที่จะติดตั้ง:

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

คุณยังสามารถส่งออกpkg-configไฟล์ ไฟล์นี้อนุญาตให้แอปพลิเคชันบุคคลที่สามนำเข้าไลบรารีของคุณได้อย่างง่ายดาย:

สร้างไฟล์เท็มเพลตชื่อmylib.pc.in(ดูpc (5) manpage สำหรับข้อมูลเพิ่มเติม):

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}

ในของคุณCMakeLists.txtเพิ่มกฎเพื่อขยาย@มาโคร ( @ONLYขอให้ cmake เพื่อไม่ขยายตัวแปรของแบบฟอร์ม${VAR}):

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

และสุดท้ายติดตั้งไฟล์ที่สร้างขึ้น:

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

นอกจากนี้คุณยังอาจจะใช้CMakeEXPORTคุณลักษณะ อย่างไรก็ตามฟีเจอร์นี้ใช้ได้กับcmakeและฉันพบว่ามันยากที่จะใช้

สุดท้ายทั้งหมดCMakeLists.txtควรมีลักษณะดังนี้:

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)

12
เพียงแค่เสริมคำอธิบายที่ยอดเยี่ยมของ @ Jezz: หลังจากขั้นตอนทั้งหมดข้างต้นโปรแกรมเมอร์สามารถสร้างและติดตั้งไลบรารีโดยmkdir build && cd build/ && cmake .. && sudo make install(หรือsudo make install/stripเพื่อติดตั้งเวอร์ชันไลบรารีลาย )
silvioprog

1
คุณมีเทคนิคในการส่งต่อการอ้างอิงไลบรารีหรือไม่? ตัวอย่างเช่นถ้า mylib ขึ้นอยู่กับ liblog4cxx วิธีใดเป็นวิธีที่ดีในการไหลผ่านไปยัง mylib.pc
เม.ย.

1
@mpr หาก liblog4cxx ให้.pcไฟล์เพิ่มRequires: liblog4cxxที่คุณmylib.pcอื่นคุณก็สามารถเพิ่มการ-llog4cxx Libs:
Jérôme Pouiller

ฉันจะใช้ไลบรารีนี้ในโครงการอื่นได้อย่างไร คุณช่วยขยายตัวอย่างของคุณได้ไหม
Damir Porobic

และคุณเพิ่มส่วนหัวทั้งหมดใน PUBLIC_HEADER หรือเฉพาะส่วนที่ผู้ใช้รายอื่นควรจะเห็น? ถ้าไม่คุณจะทำอย่างไรกับส่วนหัวอื่น ๆ ทั้งหมด
Damir Porobic

87

CMakeLists.txtไฟล์ขั้นต่ำนี้รวบรวมไลบรารีที่ใช้ร่วมกันอย่างง่าย:

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)

อย่างไรก็ตามฉันไม่มีประสบการณ์ในการคัดลอกไฟล์ไปยังปลายทางอื่นด้วย CMake คำสั่ง file ที่มีลายเซ็น COPY / INSTALL ดูเหมือนว่าจะมีประโยชน์


36
CMAKE_BUILD_TYPE ควรละเว้นดังนั้นการตัดสินใจขึ้นอยู่กับผู้รวบรวม
ManuelSchneid3r

การระบุ${CMAKE_CURRENT_SOURCE_DIR}/ในinclude_directoriesนั้นมีประโยชน์หรือไม่?
Jérôme Pouiller

@Jezz ฉันไม่คิดอย่างนั้นไดเรกทอรีเดียวกันจะรวมอยู่โดยไม่มีคำนำหน้า อย่างไรก็ตามจะมีความสำคัญหากคุณอยู่ในไดเรกทอรีย่อย
Arnav Borborah

แล้วถ้าฉันต้องการผสมแหล่งที่มาและส่วนหัวของฉันในไดเร็กทอรี "ต้นทาง" ทั่วไปล่ะ มีความเป็นไปได้ในการสร้างไดเร็กทอรี "ส่วนหัว" จากแหล่งที่มาของฉันหรือไม่ (อาจติดตั้งคำสั่ง)
Sandburg

24

ฉันกำลังพยายามเรียนรู้วิธีการทำสิ่งนี้ด้วยตัวเองและดูเหมือนว่าคุณสามารถติดตั้งไลบรารีได้ดังนี้:

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

13

ขั้นแรกนี่คือเค้าโครงไดเร็กทอรีที่ฉันใช้:

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

หลังจากผ่านไปสองสามวันนี่เป็นวิธีที่ฉันชอบที่สุดในการทำสิ่งนี้ด้วย CMake สมัยใหม่:

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 และติดตั้งไลบรารีไม่จำเป็นต้องใช้ไฟล์ Find ***. cmake ก็สามารถใช้งานได้ดังนี้:

find_package(MyLib REQUIRED)

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

เพียงเท่านี้หากมีการติดตั้งในไดเร็กทอรีมาตรฐานจะพบและไม่จำเป็นต้องทำอะไรอีก หากได้รับการติดตั้งในเส้นทางที่ไม่ได้มาตรฐานก็ทำได้ง่ายเช่นกันเพียงบอก CMake ว่าจะหา MyLibConfig.cmake ได้ที่ไหนโดยใช้:

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

ฉันหวังว่านี่จะช่วยทุกคนได้มากเท่าที่ช่วยฉันได้ วิธีการเก่า ๆ ในการทำเช่นนี้ค่อนข้างยุ่งยาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.