如何使用cmake创建共享库?

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

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

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

我发现的大多数示例都是用一些共享库编译可执行文件,但从来都不是简单的共享库。 如果有人能告诉我一个使用cmake的非常简单的库,那也会很有帮助,所以我可以用这个作为例子
280768 次浏览

这个最小的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签名的文件命令看起来可能很有用。

我正在学习如何自己做到这一点,似乎你可以这样安装库:

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

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

cmake_minimum_required(VERSION 3.9)

您应该声明一个项目。cmake表示它是强制的,它将定义方便的变量PROJECT_NAMEPROJECT_VERSIONPROJECT_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")不是很方便。因此,在包含的目录中传递一个top目录:

target_include_directories(mylib PRIVATE .)

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

为库创建安装规则。我建议使用GNUInstallDirs中定义的变量CMAKE_INSTALL_*DIR:

include(GNUInstallDirs)

并声明要安装的文件:

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

你也可以导出一个pkg-config文件。该文件允许第三方应用程序轻松导入你的库:

创建一个名为mylib.pc.in的模板文件(更多信息参见< em >电脑(5)< / em >从):

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 ask to 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)

编辑

正如评论中提到的,为了符合标准,您应该能够生成静态库和共享库。这个过程有点复杂,与最初的问题不匹配。但值得一提的是,它被大大解释在这里

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

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

我希望这对每个人都有帮助,就像它帮助了我一样。旧的方法是相当麻烦的。