如何使用cmake创建共享库?


141

我已经编写了一个库,该库过去是使用自写的Makefile进行编译的,但是现在我想切换到cmake。树看起来像这样(我删除了所有不相关的文件):

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

因此,我想做的就是将源代码编译到共享库中,然后将其与头文件一起安装。

我发现的大多数示例都使用一些共享库来编译可执行文件,而不仅仅是一个普通的共享库。如果有人可以告诉我一个使用cmake的非常简单的库,这也将很有帮助,因此我可以以此为例。



相同的问题,但是有没有办法使我的源代码保持混合状态(在同一源代码目录中的.h和.cpp),但是让Cmake生成包含其工作成果的包含目录?
桑德堡

Answers:


204

始终指定以下版本的最低要求 cmake

cmake_minimum_required(VERSION 3.9)

您应该声明一个项目。cmake表示这是强制性的,它将定义方便的变量PROJECT_NAMEPROJECT_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将是的符号链接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)联机帮助页):

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)

您也可以使用cmake EXPORT功能。但是,此功能仅与兼容,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)

10
只是对@Jezz的出色解释的补充:在完成上述所有步骤之后,程序员可以按以下方式构建和安装库mkdir build && cd build/ && cmake .. && sudo make install(或sudo make install/strip安装带区带库的版本)。
silvioprog

1
您是否具有传递库依赖关系的技术?例如,如果mylib依赖于liblog4cxx,什么是将其一直流到mylib.pc的好方法?
mpr

1
@mpr如果liblog4cxx提供了.pc文件,则将其添加Requires: liblog4cxx到您的文件中,mylib.pc否则,您可以将其添加-llog4cxx到中Libs:
杰罗姆Pouiller

我如何在另一个项目中使用该库?您可以扩展您的示例吗?
Damir Porobic '18

并且您将所有标头添加到PUBLIC_HEADER还是仅其他用户应该可以看到的标头?如果不是,您如何处理所有其他标头?
Damir Porobic '18

84

这个最小的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将文件复制到其他目标的经验。带有COPY / INSTALL签名的file命令看起来可能有用。


34
CMAKE_BUILD_TYPE应该省略,因此决定取决于编译者。
ManuelSchneid3r

指定是否${CMAKE_CURRENT_SOURCE_DIR}/include_directories是有用的?
杰罗姆Pouiller

@Jezz我不这样认为,不带前缀就包含相同的目录。但是,如果您在子目录中,那将很重要。
Arnav Borborah '18

如果要在通用的“源”目录中混合源和标头,该怎么办?从我的来源创建“标头”目录是否有“后期生成”的可能性?(可能是安装命令)
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})

12

首先,这是我正在使用的目录布局:

.
├── 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 ..

我希望这能对所有人有所帮助。这样做的旧方法非常麻烦。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.