Сборка и установка статической и разделяемой (shared) библиотеки С++ с помощью одного проекта CMake. Библиотеки С++ и CMake. Часть 4
CMake предоставляет программисту богатый инструментарии для сборки и установки библиотек и программ, в том числе и возможность установить статическую и разделяемую (shared) версии библиотек в одну папку.
Сегодня мы модернизируем скрипты CMake, чтобы упростить настройку и внесение изменений в проект, а также рассмотрим одновременную установку статической и разделяемой версий нашей библиотеки.
Подготовка проектов
В репозиторий проектов блога:
https://gitflic.ru/project/vasiliyaltunin/articles_blog_altuninvv_ru
я добавил папку с базовой версией проекта:
Мы будем использовать его для создания библиотеки.
Удалите старые папки с проектами, если они остались:
colorconsole
colorconsolelibКлонируем проект из GitFlic:
cd c:\projects\
git clone https://gitflic.ru/project/vasiliyaltunin/articles_blog_altuninvv_ru.git --depth 1Если у вас команда git не работает, возможно, вам сначала придется установить git в Msys2:
pacman -S gitСкопируем папку с проектом:
xcopy /e/y .\articles_blog_altuninvv_ru\qt6\colorconsole\ .\colorconsole\ Соберем и проверим что все работает нормально:
cd c:\projects\colorconsole
cmake -S . -B build
cmake --build build
.\build\colorconsoleapp.exeВсё в порядке.
Создание проекта для библиотеки
Создадим копию папки проекта для нашей библиотеки:
cd c:\projects
xcopy /e/y c:\projects\colorconsole c:\projects\colorconsolelib\На данном этапе мы создаем несколько новых файлов и папок. Некоторые из них будут находится в папке cmake в корне нашей библиотеки. По умолчанию в проектах CMake эта папка используется для хранения разнообразных настроек, чтобы не захламлять корень проекта кучей файлов.
Наша задача – разбить старый файл CMakeFiles.txt на несколько меньших и поместить в них инструкции, соответствующие названию файла. Таким образом нам не придется больше листать «простыню» с текстом в одном файле. Если нам потребуется изменить настройки проекта, мы просто можем изменить файл с нужным названием!
Удалим ненужные файлы и папки:
cd c:\projects\colorconsolelib
rmdir /s/q build
del main.cppСоздадим папки и скопируем файлы:
mkdir src
mkdir include\colorconsole
mkdir cmake
move *.cpp src
copy *.h include\colorconsole
move *.h srcОбратите внимание! Мы создаем папку:
include\colorconsoleВ предыдущих частях мы не уделяли этому внимание, да и файл у нас всего один, но хорошим тоном является хранить все свои заголовочные файлы в отдельной папке имя которой, совпадает с именем библиотеки. На самом деле, у библиотеки может быть несколько заголовочных файлов, чтобы не захламлять корневую папку заголовочных файлов операционной системы, лучше всего создавать отдельную папку для своих файлов.
Создадим требуемые файлы:
type nul > cmake\install.cmake.in
type nul > cmake\libconf.cmake.in
type nul > cmake\settings.cmake.in
type nul > cmake\targets.cmake.in
type nul > cmake\colorconsole-config.cmake.inОткроем проект библиотеки в VSCode:
cd c:\projects\colorconsolelib\
code .Изменим содержимое файла CMakeLists.txt:
cmake_minimum_required(VERSION 3.14)
# Имя оно же target проекта, его описание и язык
project(colorconsole
VERSION 0.1.0
DESCRIPTION "Библиотека для вывода цветного текста в консоль"
LANGUAGES CXX)
# Загружаем данные о путях до папок в которые установлены библиотеки C++
include(GNUInstallDirs)
# Загружаем модули CMake
include(GenerateExportHeader)
include(CMakePackageConfigHelpers)
# Задаем переменные и настройки для использования
include(${PROJECT_SOURCE_DIR}/cmake/settings.cmake.in)
# Задаем настройки самой библиотеки и её тип
include(${PROJECT_SOURCE_DIR}/cmake/libconf.cmake.in)
# Задаем targets для библиотеки
include(${PROJECT_SOURCE_DIR}/cmake/targets.cmake.in)
# Задаем настройки установки для библиотеки
include(${PROJECT_SOURCE_DIR}/cmake/install.cmake.in)
Здесь мы просто указываем какие файлы включать в основной. В результате получится всё та же "простыня" из текста, но только в момент разбора файла конфигурации самим CMake.
Первый файл это settings.cmake.in - в него мы поместили все настройки, теперь если нужно перенастроить проект, нам достаточно открыть и изменить только этот файл.
Откроем файл settings.cmake.in и заменим содержимое:
# Задаем версию для сборки OFF - static, ON - shared
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
message(BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS})
# Определяем тип сборки для последующего использования
if(BUILD_SHARED_LIBS STREQUAL "ON")
message(" === Library build set to SHARED")
set(LIB_LINK_TYPE "shared")
else()
message(" === Library build set to STATIC")
set(LIB_LINK_TYPE "static")
endif()
# Задаем пространство имен
set("MAIN_NAMESPACE" "ccon")
set("PROJECT_NAMESPACE" "colorconsole")
# Префикс который используется для файлов конфигурации cmake
set("MY_CMAKE_PREFIX" "colorconsole")
# Имя файла для библиотеки к нему автоматически будет добавлено lib
set("MY_LIB_OUTPUT_NAME" "colorconsole")
# Задаем путь для установки CMake-файлов проекта
set("COLORCONSOLE_INSTALL_CMAKEDIR" "${CMAKE_INSTALL_LIBDIR}/cmake/${MY_CMAKE_PREFIX}" CACHE STRING
"Install path for colorconsole package-related CMake files"
)
# Имена файлов, содержащие исходный код библиотеки
set(sources
src/colorconsole.cpp
src/colorconsole.h
)
# Имя файла с настройками для статической сборки библиотеки
set(targets_file "${MY_CMAKE_PREFIX}-${LIB_LINK_TYPE}-targets.cmake")
# Требования к версии C++ и поддержки стандарта
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
message(" === Settings setup complete")
Здесь мы используем опцию, подробно их применение мы рассмотрим в одной из будущих статей. Для сборки статической библиотеки мы, при конфигурировании проекта, можем задать опцию:
cmake -S . -B build -DBUILD_SHARED_LIBS=OFFПри указании флага ON или, если мы не указываем опцию, в нашем случае будет произведена сборка разделяемой библиотеки.
В случае с обычной программой опции работают аналогично, но в этом случае она указывает каким образом нужно собирать библиотеки с программами.
В файле libconf.cmake.in мы настраиваем проект как библиотеку и задаем условия для сборки как статической или как разделяемой библиотеки, а так же создаем файл конфигурации colorconsole-config.cmake для нашей библиотеки.
Откроем файл libconf.cmake.in и заменим содержимое:
# Инициализируем проект как библиотеку
# Проверяем сборку, если задана переменная BUILD_SHARED_LIBS
# то собираем как SHARED иначе STATIC
if(BUILD_SHARED_LIBS)
add_library(${PROJECT_NAME} SHARED)
message(" Library will be build as SHARED")
else()
add_library(${PROJECT_NAME} STATIC)
message(" Library will be build as STATIC")
endif()
# Инициализируем пространство имен и алиас
add_library(${MAIN_NAMESPACE}::${PROJECT_NAMESPACE} ALIAS ${PROJECT_NAME})
# Создаем файл конфигурации и указываем папку для его установки
configure_package_config_file(cmake/${MY_CMAKE_PREFIX}-config.cmake.in colorconsole-config.cmake
INSTALL_DESTINATION "${COLORCONSOLE_INSTALL_CMAKEDIR}"
)
# Создаем файл конфигурации с указанием версии нашей библиотеки и указываем папку для его установки
write_basic_package_version_file(${MY_CMAKE_PREFIX}-config-version.cmake
COMPATIBILITY SameMajorVersion
VERSION ${PACKAGE_VERSION}
)
message(" === Libconf settings complete")Здесь мы просто проверяем значение переменной LIB_LINK_TYPE и в зависимости от него выставляем тип нашей библиотеки.
В файле targets.cmake.in мы задаем настройки для целей (target) нашего проекта, в основном здесь задаются файлы и папки, которые нужно включить в проект, а так же имя будущей библиотеки.
Откроем файл targets.cmake.in и заменим содержимое:
# Добавляем файлы с исходным кодом к нашему проекту
target_sources(${PROJECT_NAME} PRIVATE ${sources})
# Добавляем папку src к нашему проекту
target_include_directories(${PROJECT_NAME}
PRIVATE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>"
)
# Если библиотека используется как папка, открываем доступ к заголовочным файлам
target_include_directories(${PROJECT_NAME}
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
)
# Имя файла библиотеки. К нему автоматически будет добавлено lib
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${MY_LIB_OUTPUT_NAME}")
message(" === Targets settings complete")В файле install.cmake.in мы указываем что и куда должно быть установлено, а тк же задаем правила для установки.
Откроем файл install.cmake.in и заменим содержимое:
# Создаем правила для установки проекта
install(TARGETS ${PROJECT_NAME}
EXPORT lib_export
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
# Указываем что нужно скопировать содержимое каталога ./include и что это заголовочные файлы
install(DIRECTORY include/
TYPE INCLUDE
)
# Создаем правила установки файла *-static-targets.cmake
install(EXPORT lib_export
FILE "${targets_file}"
DESTINATION "${COLORCONSOLE_INSTALL_CMAKEDIR}"
NAMESPACE "${MAIN_NAMESPACE}::"
)
# Создаем правила для установки файлов *-config.cmake и *-config-version.cmake
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${MY_CMAKE_PREFIX}-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${MY_CMAKE_PREFIX}-config-version.cmake"
DESTINATION "${COLORCONSOLE_INSTALL_CMAKEDIR}"
)
message(" === Install settings complete")Файл colorconsole-config.cmake.in не используется при сборке нашей библиотеки, но он необходим для включения нашей библиотеки в сторонние проекты, так как содержит правила, определяющие тип сборки нашей библиотеки со сторонним проектом.
Откроем файл colorconsole-config.cmake.in и изменим содержимое:
# CMake package config file for colorconsole library.
#
# The following target is imported:
#
# ccon:colorconsole
#
# Запускаем инициализацию
@PACKAGE_INIT@
# Проверяем указана ли сборка как разделяемой библиотеки
if(BUILD_SHARED_LIBS)
set(type "shared")
else()
set(type "static")
endif()
# Указываем включить файл конфигурации созданный в основном файле CMakeLists.txt
# В зависимости от выбранной конфигурации это может быть static или shared
include("${CMAKE_CURRENT_LIST_DIR}/colorconsole-${type}-targets.cmake")
# Проверяем что все требуемые компоненты найдены
check_required_components(colorconsole)
# Выводим строку при конфигурировании проекта использующего наше библиотеку
message(" === Found ${type} colorconsole (version ${${CMAKE_FIND_PACKAGE_NAME}_VERSION})")Сборка разделяемой версии библиотеки
Запустим конфигурирование:
cmake -S . -B buildBUILD_SHARED_LIBS=ON
=== Library build set to SHARED
=== Settings setup complete
Library will be build as SHARED
=== Libconf settings complete
=== Targets settings complete
=== Install settings complete
-- Configuring done (0.3s)
-- Generating done (0.1s)
-- Build files have been written to: C:/projects/colorconsolelib/buildКак видите, теперь у нас выводиться дополнительная информация о процессе конфигурирования.
Так как у нас по умолчанию указана сборка библиотеки как разделяемой, то и в логах мы видим тип - SHARED.
Запустим сборку:
cmake --build build[2/2] Linking CXX shared library libcolorconsole.dllУ нас будет собрана разделяемая версия проекта. В Windows это файлы с расширением .dll.
Установим библиотеку в папку:
cmake --install build --prefix=..\libcolorconsole-- Install configuration: "Debug"
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/libcolorconsole.dll.a
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/bin/libcolorconsole.dll
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/include
-- Up-to-date: C:/projects/colorconsolelib/..\libcolorconsole/include/colorconsole
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/include/colorconsole/colorconsole.h
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/cmake/colorconsole/colorconsole--targets.cmake
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/cmake/colorconsole/colorconsole--targets-debug.cmake
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/cmake/colorconsole/colorconsole-config.cmake
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/cmake/colorconsole/colorconsole-config-version.cmakeСборка статической версии библиотеки
Обязательно при изменении типа сборки удаляем папку build:
rmdir /s/q buildПоменяем в файле settings.cmake.in строку:
cmake -S . -B build -DBUILD_SHARED_LIBS=OFFBUILD_SHARED_LIBS=OFF
=== Library build set to STATIC
=== Settings setup complete
Library will be build as STATIC
=== Libconf settings complete
=== Targets settings complete
=== Install settings complete
-- Configuring done (0.3s)
-- Generating done (0.2s)
-- Build files have been written to: C:/projects/colorconsolelib/buildПересоберем:
cmake --build build[2/2] Linking CXX static library libcolorconsole.aЗапустим установку в папку:
cmake --install build --prefix=..\libcolorconsole-- Install configuration: "Debug"
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/libcolorconsole.a
-- Up-to-date: C:/projects/colorconsolelib/..\libcolorconsole/include
-- Up-to-date: C:/projects/colorconsolelib/..\libcolorconsole/include/colorconsole
-- Up-to-date: C:/projects/colorconsolelib/..\libcolorconsole/include/colorconsole/colorconsole.h
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/cmake/colorconsole/colorconsole-static-targets.cmake
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/cmake/colorconsole/colorconsole-static-targets-debug.cmake
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/cmake/colorconsole/colorconsole-config.cmake
-- Installing: C:/projects/colorconsolelib/..\libcolorconsole/lib/cmake/colorconsole/colorconsole-config-version.cmakeЕсли мы проверим содержимое файлов:
colorconsole-config.cmake
colorconsole-config-version.cmakeТо окажется что они по сути не меняются, таким образом мы можем последовательно собрать и установить две версии библиотеки и они не будут конфликтовать!
Статическая и динамическая сборка проекта CMAKE с библиотекой C++ с использованием CMake
Откроем наш проект:
cd c:\projects\colorconsoleОбязательно удалим папку build и старые файлы:
rmdir /s/q build
del colorconsole.h colorconsole.cpp
code .Изменим файл CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(colorconsoleapp VERSION 0.1 LANGUAGES CXX)
# Путь для поиска библиотеки с использованием find_package()
list(APPEND CMAKE_PREFIX_PATH "..\\libcolorconsole\\lib\\cmake\\colorconsole\\")
find_package(colorconsole CONFIG REQUIRED)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME}
PRIVATE
main.cpp
)
include(GNUInstallDirs)
target_link_libraries(${PROJECT_NAME} PRIVATE ccon::colorconsole)
# Путь к заголовочным файлам библиотеки
target_include_directories(${PROJECT_NAME} PRIVATE "..\\libcolorconsole\\include")
message(BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS})Внесем изменения в файл mian.cpp, изменим путь до заголовочного файла:
#include "colorconsole/colorconsole.h"Запустим конфигурирование:
cmake -S . -B build[cmake] === Found static colorconsole (version 0.1.0)
[cmake] BUILD_SHARED_LIBS=
[cmake] -- Configuring done (0.3s)
[cmake] -- Generating done (0.1s)
[cmake] -- Build files have been written to: C:/projects/colorconsole/buildОбратите внимание, несмотря на то, что по умолчанию библиотека собирается как разделяемая (shared) CMake выбрал для нашего проекта статическую версию. Почему?
Ответ в строке:
[cmake] BUILD_SHARED_LIBS=Ранее мы указали в конфигурации CMake строку:
message(BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS})Полученный результат означает, что по умолчанию переменная:
BUILD_SHARED_LIBS Не установлена.
Установка переменной BUILD_SHARED_LIBS в проекте CMake
Единственно правильный способ установки переменной BUILD_SHARED_LIBS это использование опции:
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)Добавим в файл CMakeLists.txt нашего проекта перед строкой:
find_package(colorconsole CONFIG REQUIRED) Строку:
# Опция указывающая тип сборки с внешними библиотеками ON - shared, OFF - static
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)Запустим конфигурирование:
cmake -S . -B buildBUILD_SHARED_LIBS=ON
=== Found shared colorconsole (version 0.1.0)
BUILD_SHARED_LIBS=ON
-- Configuring done (0.2s)
-- Generating done (0.1s)
-- Build files have been written to: C:/projects/colorconsole/buildЗапустим сборку:
cmake --build build
[2/2] Linking CXX executable colorconsoleapp.exeПроверим:
ldd .\build\colorconsoleapp.exe
ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x7ff81b290000)
KERNEL32.DLL => /c/Windows/System32/KERNEL32.DLL (0x7ff81a0e0000)
KERNELBASE.dll => /c/Windows/System32/KERNELBASE.dll (0x7ff818b50000)
msvcrt.dll => /c/Windows/System32/msvcrt.dll (0x7ff819600000)
libstdc++-6.dll => /mingw64/bin/libstdc++-6.dll (0x7fffe90a0000)
libwinpthread-1.dll => /mingw64/bin/libwinpthread-1.dll (0x7ff8048d0000)
libgcc_s_seh-1.dll => /mingw64/bin/libgcc_s_seh-1.dll (0x7ff803c40000)
libcolorconsole.dll => /mingw64/bin/libcolorconsole.dll (0x7ff802f70000)Строка:
libcolorconsole.dll => /mingw64/bin/libcolorconsole.dll (0x7ff802f70000)Указывает, что мы используем разделяемую библиотеку.
В файле CMakeLists.txt нашего проекта изменим строку на:
option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)Мы указали - OFF.
Удалим папку сборки проекта, это важно:
rmdir /s/q buildЗапустим конфигурирование:
cmake -S . -B build-- Building for: Ninja
-- The CXX compiler identification is GNU 15.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
=== Found static colorconsole (version 0.1.0)
BUILD_SHARED_LIBS=OFF
-- Configuring done (3.0s)
-- Generating done (0.1s)
-- Build files have been written to: C:/projects/colorconsole/buildНа этот раз используется статическая версия библиотеки.
Запустим сборку:
cmake --build buildПроверим:
ldd .\build\colorconsoleapp.exe ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x7ff81b290000)
KERNEL32.DLL => /c/Windows/System32/KERNEL32.DLL (0x7ff81a0e0000)
KERNELBASE.dll => /c/Windows/System32/KERNELBASE.dll (0x7ff818b50000)
msvcrt.dll => /c/Windows/System32/msvcrt.dll (0x7ff819600000)
libstdc++-6.dll => /mingw64/bin/libstdc++-6.dll (0x7fffe90a0000)
libwinpthread-1.dll => /mingw64/bin/libwinpthread-1.dll (0x7ff8048d0000)
libgcc_s_seh-1.dll => /mingw64/bin/libgcc_s_seh-1.dll (0x7ff803c40000)Проект был собран со статической версией библиотеки, так как в списке нет файла libcolorconsole.dll
Заключение
Сегодня мы рассмотрели установку одновременно статической и разделяемой (shared) версий библиотеки с помощью CMake:
Получили исходный код проекта из git-репозитория на GitFlic;
На его базе создали библиотеку;
Разбили файл CMakeLists.txt на несколько маленьких и включили их в основную библиотеку;
Добавили опцию BUILD_SHARED_LIBS в нашу библиотеку и код для её обработки;
Собрали библиотеку с включенной и отключенной опцией BUILD_SHARED_LIBS и установили его в отдельную папку;
Модернизировали старый проект и собрали его со статической и разделяемой версиями библиотеки.
Добавить комментарий