[英]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_NAME
、 PROJECT_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 ...)
.请避免使用
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:此文件允许第三方应用程序轻松导入您的库:
pkg-config
pkg-config
PKG_CHECK_MODULES
PKG_CHECK_MODULES
pkg_check_modules
pkg_check_modules
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.