How to create a shared library with cmake?

前端 未结 4 929
死守一世寂寞
死守一世寂寞 2020-11-28 17:10

I have written a library that I used to compile using a self-written Makefile, but now I want to switch to cmake. The tree looks like this (I removed all the irrelevant file

相关标签:
4条回答
  • 2020-11-28 17:45

    I'm trying to learn how to do this myself, and it seems you can install the library like this:

    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})
    
    0 讨论(0)
  • 2020-11-28 17:48

    Always specify the minimum required version of cmake

    cmake_minimum_required(VERSION 3.9)
    

    You should declare a project. cmake says it is mandatory and it will define convenient variables PROJECT_NAME, PROJECT_VERSION and PROJECT_DESCRIPTION (this latter variable necessitate cmake 3.9):

    project(mylib VERSION 1.0.1 DESCRIPTION "mylib description")
    

    Declare a new library target. Please avoid the use of file(GLOB ...). This feature does not provide attended mastery of the compilation process. If you are lazy, copy-paste output of ls -1 sources/*.cpp :

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

    Set VERSION property (optional but it is a good practice):

    set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})
    

    You can also set SOVERSION to a major number of VERSION. So libmylib.so.1 will be a symlink to libmylib.so.1.0.0.

    set_target_properties(mylib PROPERTIES SOVERSION 1)
    

    Declare public API of your library. This API will be installed for the third-party application. It is a good practice to isolate it in your project tree (like placing it include/ directory). Notice that, private headers should not be installed and I strongly suggest to place them with the source files.

    set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)
    

    If you work with subdirectories, it is not very convenient to include relative paths like "../include/mylib.h". So, pass a top directory in included directories:

    target_include_directories(mylib PRIVATE .)
    

    or

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

    Create an install rule for your library. I suggest to use variables CMAKE_INSTALL_*DIR defined in GNUInstallDirs:

    include(GNUInstallDirs)
    

    And declare files to install:

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

    You may also export a pkg-config file. This file allows a third-party application to easily import your library:

    • with Makefile, see pkg-config
    • with Autotools, see PKG_CHECK_MODULES
    • with cmake, see pkg_check_modules

    Create a template file named mylib.pc.in (see pc(5) manpage for more information):

    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}
    

    In your CMakeLists.txt, add a rule to expand @ macros (@ONLY ask to cmake to not expand variables of the form ${VAR}):

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

    And finally, install generated file:

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

    You may also use cmake EXPORT feature. However, this feature is only compatible with cmake and I find it difficult to use.

    Finally the entire CMakeLists.txt should looks like:

    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)
    
    0 讨论(0)
  • 2020-11-28 17:52

    First, this is the directory layout that I am using:

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

    After a couple of days taking a look into this, this is my favourite way of doing this thanks to modern 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)
    

    After running CMake and installing the library, there is no need to use Find***.cmake files, it can be used like this:

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

    That's it, if it has been installed in a standard directory it will be found and there is no need to do anything else. If it has been installed in a non-standard path, it is also easy, just tell CMake where to find MyLibConfig.cmake using:

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

    I hope this helps everybody as much as it has helped me. Old ways of doing this were quite cumbersome.

    0 讨论(0)
  • 2020-11-28 18:09

    This minimal CMakeLists.txt file compiles a simple shared library:

    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)
    

    However, I have no experience copying files to a different destination with CMake. The file command with the COPY/INSTALL signature looks like it might be useful.

    0 讨论(0)
提交回复
热议问题