Понедельник, 01.09.2025 19:00

Сборка и установка статической и разделяемой (shared) библиотеки С++ с помощью одного проекта CMake. Библиотеки С++ и CMake. Часть 4

Сборка и установка статической и разделяемой (shared) библиотеки С++ с помощью одного проекта CMake. Библиотеки С++ и CMake. Часть 4

CMake предоставляет программисту богатый инструментарии для сборки и установки библиотек и программ, в том числе и возможность установить статическую и разделяемую (shared) версии библиотек в одну папку.

Сегодня мы модернизируем скрипты CMake, чтобы упростить настройку и внесение изменений в проект, а также рассмотрим одновременную установку статической и разделяемой версий нашей библиотеки.

Подготовка проектов

В репозиторий проектов блога:

https://gitflic.ru/project/vasiliyaltunin/articles_blog_altuninvv_ru

я добавил папку с базовой версией проекта:

https://gitflic.ru/project/vasiliyaltunin/articles_blog_altuninvv_ru/file?file=qt6%2Fcolorconsole&branch=master

Мы будем использовать его для создания библиотеки.

Удалите старые папки с проектами, если они остались:

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 build
BUILD_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=OFF
BUILD_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 build
BUILD_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 и установили его в отдельную папку;

Модернизировали старый проект и собрали его со статической и разделяемой версиями библиотеки.

Категория C++
Теги Cpp CMAKE

Добавить комментарий

Простой текст

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Строки и абзацы переносятся автоматически.
  • Адреса веб-страниц и email-адреса преобразовываются в ссылки автоматически.
Просмотров: 269