简体   繁体   English

如何使用 cmake 创建共享库?

[英]How to create a shared library with cmake?

I have written a library that I used to compile using a self-written Makefile, but now I want to switch to cmake.我已经编写了一个库,我曾经使用自己编写的 Makefile 进行编译,但现在我想切换到 cmake。 The tree looks like this (I removed all the irrelevant files):树看起来像这样(我删除了所有不相关的文件):

.
├── 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 的非常简单的库,那也会很有帮助,所以我可以以此为例。

Always specify the minimum required version of cmake始终指定最低要求的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): cmake说这是强制性的,它将定义方便的变量PROJECT_NAMEPROJECT_VERSIONPROJECT_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 ...) .请避免使用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 :如果你懒惰,复制粘贴ls -1 sources/*.cpp

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

Set VERSION property (optional but it is a good practice):设置VERSION属性(可选,但这是一个好习惯):

set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})

You can also set SOVERSION to a major number of VERSION .您还可以将SOVERSION设置为VERSION的主要编号。 So libmylib.so.1 will be a symlink to libmylib.so.1.0.0 .所以libmylib.so.1将是一个符号链接到libmylib.so.1.0.0

set_target_properties(mylib PROPERTIES SOVERSION 1)

Declare public API of your library.声明您的库的公共 API。 This API will be installed for the third-party application.将为第三方应用程序安装此 API。 It is a good practice to isolate it in your project tree (like placing it include/ directory).将它隔离在您的项目树中是一个很好的做法(例如将其放置在include/目录中)。 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" .如果您使用子目录,则包含诸如"../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 :我建议使用变量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-config file.您还可以导出pkg-config文件。 This file allows a third-party application to easily import your library:此文件允许第三方应用程序轻松导入您的库:

Create a template file named mylib.pc.in (see pc(5) manpage for 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 ( @ONLY ask to cmake to not expand variables of the form ${VAR} ):在您的CMakeLists.txt ,添加一个规则来扩展@宏( @ONLY要求 cmake 不扩展${VAR}形式的${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 .您也可以使用cmake EXPORT功能 However, this feature is only compatible with cmake and I find it difficult to use.不过这个功能只兼容cmake ,我觉得很难用。

Finally the entire CMakeLists.txt should 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)

EDIT编辑

As mentioned in comments, to comply with standards you should be able to generate a static library as well as a shared library.正如评论中提到的,为了符合标准,您应该能够生成静态库和共享库。 The process is bit more complex and does not match with the initial question.这个过程有点复杂,与最初的问题不符。 But it worths to mention that it is greatly explained here .但它实在值得一提的是它大大地解释这里

This minimal CMakeLists.txt file 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.但是,我没有使用 CMake 将文件复制到其他目的地的经验。 The file command with the COPY/INSTALL signature looks like it might be useful.带有 COPY/INSTALL 签名的 file 命令看起来可能很有用。

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

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.这样做的旧方法非常麻烦。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM