C++ 如何使用 cmake 创建共享库?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17511496/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:14:58  来源:igfitidea点击:

How to create a shared library with cmake?

c++compilationcmakeshared-libraries

提问by Florian M

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 files):

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

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

So what I am trying to do is just to compile the source into a shared library and then install it with the header files.

所以我想要做的只是将源代码编译成一个共享库,然后用头文件安装它。

Most examples that I have found compile executables with some shared libraries but never just a plain shared library. It would also be helpful if someone could just tell me a very simple library that uses cmake, so I can use this as an example.

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

回答by Jér?me Pouiller

Always specify the minimum required version of cmake

始终指定所需的最低版本 cmake

cmake_minimum_required(VERSION 3.9)

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

你应该声明一个项目。cmake说这是强制性的,它将定义方便的变量PROJECT_NAMEPROJECT_VERSION并且PROJECT_DESCRIPTION(后一个变量需要 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:

声明一个新的库目标。请避免使用file(GLOB ...). 此功能不提供编译过程的有人参与的掌握。如果你很懒惰,复制粘贴输出ls -1 sources/*.cpp

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

Set VERSIONproperty (optional but it is a good practice):

设置VERSION属性(可选,但这是一个好习惯):

set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})

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

您还可以设置SOVERSIONVERSION. 所以libmylib.so.1将是一个符号链接到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.

声明您的库的公共 API。将为第三方应用程序安装此 API。将它隔离在您的项目树中是一个很好的做法(例如将其放置在include/目录中)。请注意,不应安装私有头文件,我强烈建议将它们与源文件放在一起。

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:

如果您使用子目录,则包含诸如"../include/mylib.h". 因此,在包含的目录中传递一个顶级目录:

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_*DIRdefined in GNUInstallDirs:

为您的库创建安装规则。我建议使用中CMAKE_INSTALL_*DIR定义的变量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-configfile. This file allows a third-party application to easily import your library:

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

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

创建一个名为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}

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

在您的 中CMakeLists.txt,添加一个规则来扩展@宏(@ONLY要求 cmake 不扩展表单的变量${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 EXPORTfeature. However, this feature is only compatible with cmakeand I find it difficult to use.

您也可以使用cmakeEXPORT功能。但是,这个功能只兼容,cmake我觉得很难用。

Finally the entire CMakeLists.txtshould looks like:

最后整个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)

回答by Robert Franke

This minimal CMakeLists.txtfile compiles a simple shared library:

这个最小的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)

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.

但是,我没有使用 CMake 将文件复制到其他目的地的经验。带有 COPY/INSTALL 签名的 file 命令看起来可能很有用。

回答by gromit190

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

回答by Luis

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,这是我最喜欢的方法:

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:

运行CMake并安装库后,就不需要使用Find***.cmake文件了,可以这样使用:

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 在哪里可以找到 MyLibConfig.cmake:

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.

我希望这对每个人都有帮助,就像它对我的帮助一样。这样做的旧方法非常麻烦。