如何使用 cmake 创建共享库
How to create a shared library with cmake?
我写了一个库,我曾经使用自己编写的Makefile进行编译,但现在我想切换到cmake。树看起来像这样(我删除了所有不相关的文件(:
.
├── include
│ ├── animation.h
│ ├── buffers.h
│ ├── ...
│ ├── vertex.h
│ └── world.h
└── src
├── animation.cpp
├── buffers.cpp
├── ...
├── vertex.cpp
└── world.cpp
所以我要做的只是将源代码编译成一个共享库,然后用头文件安装它。
我找到的大多数示例都使用一些共享库编译可执行文件,但绝不仅仅是普通的共享库。如果有人能告诉我一个使用 cmake 的非常简单的库,那也会很有帮助,这样我就可以用这个作为例子。
始终指定所需的最低版本cmake
cmake_minimum_required(VERSION 3.9)
您应该声明一个项目。 cmake
说它是强制性的,它将定义方便的变量PROJECT_NAME
、PROJECT_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)
为库创建安装规则。我建议使用GNUInstallDirs
中定义的变量CMAKE_INSTALL_*DIR
:
include(GNUInstallDirs)
并声明要安装的文件:
install(TARGETS mylib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
您也可以导出pkg-config
文件。此文件允许第三方应用程序轻松导入您的库:
- 使用生成文件,请参阅
pkg-config
- 使用自动工具,请参阅
PKG_CHECK_MODULES
- 使用 cmake,请参阅
pkg_check_modules
创建一个名为 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)
编辑
如注释中所述,为了符合标准,您应该能够生成静态库和共享库。这个过程有点复杂,与最初的问题不匹配。但值得一提的是,这里对此进行了大量解释。
这个最小的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})
首先,这是我正在使用的目录布局:
.
├── 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 ..
我希望这对每个人的帮助和对我的帮助一样多。这样做的旧方法非常麻烦。
- 使用Boost Interprocess创建托管共享内存需要很长时间
- 在为LINUX创建共享库时,如何避免STL的私有/弱副本
- C++共享库:创建和使用
- 在共享缓冲区内存中创建 ::std::string 对象
- 如何创建一个共享对象与另一个.所以在Cmake
- 是否可以/希望创建不可复制的共享指针模拟(以启用weak_ptr跟踪/借用类型语义)?
- 如何防止 CMake 在构建时(而不是在安装时)为共享库创建符号链接?
- 使用Bazel C++教程;build不创建/使用共享库
- 是否可以在专用内存空间中分配一个为提升管理共享内存而创建的对象
- 在创建共享库时,是否可以明确地排除某些类
- 是否可以在并行区域中为共享 2D 数组创建选定元素的线程本地副本?(共享,私有,障碍:OPenMP)
- 为什么从引用创建共享指针会复制对象?
- Android :需要在 c++ NDK 中创建共享首选项对象并存储一些布尔值
- 如何更改在 c++ 中使用提升库创建的共享内存的路径
- 使用多个标头使用 CMake 创建C++共享库
- Qt 创建器:将库移动到 subdir 后无法打开共享对象文件
- 如何从静态c和c++库创建共享c库
- 如何使用 c++ 创建对"everyone"具有共享访问权限的文件夹
- MSVC 2017 在共享库中创建模板函数的副本
- 使用CPACK时,请防止为已安装的共享库创建Namelink