Полезные ссылки
Структура каталогов проекта
Файлы проекта и результаты компиляции размещаются в каталогах:
└── project ├── _build │ ├── debug │ │ ├── bin │ │ ├── etc │ │ ├── include │ │ ├── lib │ │ ├── log │ │ ├── share │ │ └── var │ └── release ├── .git ├── cmake │ ├── cmlib │ ├── etc │ │ └── uncrustify │ ├── find │ └── generators ├── doc ├── files │ ├── etc │ ├── log │ ├── share │ └── var ├── l10n ├── src │ ├── app │ └── lib ├── thirdparty └── tools
Назначение каталогов приведено в таблице.
Каталог | Назначение |
---|---|
|
Результаты компиляции |
|
Результаты компиляции в режиме отладки |
|
Исполняемые файлы |
|
Символическая ссылка на каталог |
|
Заголовочные файлы копируемые и генерируемые во время сборки |
|
Статические и динамические библиотеки |
|
Символическая ссылка на каталог |
|
Символическая ссылка на каталог |
|
Символическая ссылка на каталог |
|
Результаты компиляции в режиме выпуска (иерархия аналогична |
|
Системные файлы репозитория Git |
|
Файлы с дополнительными функциями для CMake |
|
Библиотека функций для CMake |
|
Файлы настроек, используемые в CMake |
|
Файл настройки для программы автоматического форматирования исходных текстов |
|
Модули CMake для поиска внешних программ и библиотек |
|
Генераторы проектов |
|
Документация для проекта |
|
Каталог для дополнительных файлов |
|
Каталог для файлов настроек проекта |
|
Каталог для журналов |
|
Каталог для неизменяемых файлов |
|
Каталог для изменяемых файлов |
|
Файлы переводов |
|
Исходные тексты |
|
Исходные тексты программы |
|
Исходные тексты библиотеки |
|
Исходные тексты дополнительных и сторонних проектов |
|
Дополнительные утилиты |
Каталог _build
создаётся, чтобы избежать попадания создаваемых во время
сборки файлов в иерархию основного проекта. Запись результатов сборки
проекта внутрь иерархии каталогов с исходными текстами приводит к
засорению формируемыми на этапе сборки файлами, которые затрудняют
разработку, поиск в оригинальных файлах и мешают ориентироваться в
проекте. При работе с несколькими типами сборки, например, отладка и
выпуск, появляется необходимость корректного полного удаления
результатов предыдущего тип сборки.
Базовый проект
Проект, в котором выполнены приведённые в данном разделе действия, можно посмотреть здесь или сделать его копию командой:
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-base
Инициализация подмодулей
Для начала нужно создать каталог для проекта, перейти в него и инициализировать репозиторий:
mkdir cmlib-example-base
cd cmlib-example-base
git init
Для подключения основных подмодулей, содержащих дополнительные функции для работы с проектом, и фиксации произведённого изменения нужно выполнить:
git submodule add https://git.246060.ru/f1x1t/cmlib.git cmake/cmlib
git submodule add https://git.246060.ru/f1x1t/cmake-find.git cmake/find
git submodule add https://git.246060.ru/f1x1t/cmake-generators.git cmake/generators
git commit -a -m "Начало проекта"
Отправить изменения в проекте на сервер и сделать ветку master
основной
(можно пропустить):
git remote add origin АДРЕС_РЕПОЗИТОРИЯ_НА_СЕРВЕРЕ
git push -u origin master
После отправки файлов на сервер отредактировать файл .gitmodules
,
так чтобы он содержал только относительные пути к подмодулям, чтобы
система автоматической сборки проекта могла их загружать без дополнительной
авторизации. Например, заменить:
[submodule "cmake/cmlib"]
path = cmake/cmlib
url = git@git.246060.ru:f1x1t/cmlib.git
[submodule "cmake/find"]
path = cmake/find
url = git@git.246060.ru:f1x1t/cmake-find.git
[submodule "cmake/generators"]
path = cmake/generators
url = git@git.246060.ru:f1x1t/cmake-generators.git
на
[submodule "cmake/cmlib"]
path = cmake/cmlib
url = ../../f1x1t/cmlib.git
[submodule "cmake/find"]
path = cmake/find
url = ../../f1x1t/cmake-find.git
[submodule "cmake/generators"]
path = cmake/generators
url = ../../f1x1t/cmake-generators.git
Обновить пути и сохранить изменения:
git submodule sync --recursive
git add .gitmodules
git commit -m "Настройка путей к подмодулям"
Загрузить шаблоны для автоматической сборки проекта в разных вариантах программных окружений и зафиксировать изменения:
wget https://git.246060.ru/f1x1t/gitlab-ci/raw/branch/master/.gitlab-ci.yml
git add .gitlab-ci.yml
git commit -m "Настройка автосборки"
Загрузить файл настройки для анализатора Clang-Tidy:
wget https://git.246060.ru/f1x1t/clang-tidy-config/raw/branch/master/.clang-tidy
git add .clang-tidy
git commit -m "Настройка Clang-Tidy"
Загрузить файл настройки для программы cmake-format, используемой для форматирования файлов CMake:
wget https://git.246060.ru/f1x1t/cmake-format/raw/branch/master/.cmake-format.py
git add .cmake-format.py
git commit -m "Настройка cmake-format"
Создать стандартные файлы и каталоги:
mkdir -p doc/breathe
touch doc/breathe/index.md.in
mkdir -p files/{etc,log,share,var}
touch files/{etc,log,share,var}/.gitkeep
git add doc files
git commit -m "Стандартные файлы и каталоги"
Файлы .gitkeep позволяют защитить каталоги от удаления
(будет выводиться дополнительное предупреждение, что каталог не пуст) и
обеспечивают возможность помещения каталогов с систему контроля версий Git,
в которой пустые каталоги недопустимы (это правильно!).
|
Создать файл .gitignore
для исключения каталогов и файлов из-под контроля Git:
wget https://git.246060.ru/f1x1t/cmlib-gitignore/raw/branch/master/.gitignore
git add .gitignore
git commit -m "Шаблон для игнорирования каталогов и файлов"
Базовые инструкции в CMake
В корневом каталоге проекта нужно создать файл CMakeLists.txt
:
# Минимальная версия CMake
cmake_minimum_required(VERSION 3.3)
# Предпочтительно следовать стандартам принятым в указанном диапазоне версий
cmake_policy(VERSION 3.0.2..3.7)
# Название и версия проекта и используемые языки программирования
project(cmlib-example-base VERSION 0.2.0 LANGUAGES C CXX)
Значение версии проекта следует формировать согласно правилам семантического версионирования.
Для подключения функций для CMake из библиотеки CMLib, нужно добавить
в файл CMakeLists.txt
строки:
###
# Обязательные переменные для CMLib
###
# Название организации
set(CMLIB_ORGANIZATION_NAME "Org." CACHE STRING "")
# Имя автора
set(CMLIB_AUTHOR_NAME "John Doe" CACHE STRING "")
# Почта автора
set(CMLIB_AUTHOR_EMAIL "mail@johndoe.com" CACHE STRING "")
# Краткое описание проекта
set(CMLIB_DESCRIPTION "Пример проекта: начало" CACHE STRING "")
# В каталоге cmake/cmlib находятся файлы с библиотечными функциями
if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/cmlib)
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/cmlib)
else()
message(FATAL_ERROR "CMake library directory does not exist")
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/find)
include(CMLibCommon)
Значения обязательных переменных, используемых библиотекой CMLib для архивирования исходных текстов, автоматического создания пакетов, генерации документации, следует отредактировать, после чего произведённые изменения можно зафиксировать:
git commit -m "Подключение библиотеки CMLib" CMakeLists.txt
Чтобы проверить корректность подключения CMLib, можно выполнить команду:
(mkdir -p _build && cd _build && cmake .. && make && echo OK)
Если последней строкой вывода будет OK
, то настройка завершена верно.
Поиск системных библиотек
Поиск программ, библиотек и заголовочных файлов, установленных в системе, можно
выполнять с помощью программы pkg-config
или функции CMake find_package
. В любом случае для указания того,
что наличие искомого объекта обязательно для сборки, используется
параметр REQUIRED
. Если требуемый компонент не будет найден, настройка проекта
завершится с ошибкой и для продолжения работы будет необходимо установить
недостающие пакеты. Кроме того можно указывать требования к версии необходимого
пакета.
Поиск с помощью программы pkg-config
Программа pkg-config
хранит базу данных параметров (обычно в каталогах
/usr/share/pkgconfig
, /usr/lib/pkgconfig
и /usr/lib/x86_64-linux-gnu/pkgconfig
),
содержащую флаги компиляции для поиска заголовочных файлов и компоновки
библиотек, установленных в систему. Для использования в CMake сначала
необходимо выполнить проверку наличия программы pkg-config
в системе
и подключить определённую в модуле PkgConfig
функцию pkg_check_modules
.
Например, для поиска библиотек gsl
, fftw3
и udev
можно написать
в файле CMakeLists.txt
:
# Поиск библиотек с помощью pkgconfig
find_package(PkgConfig REQUIRED)
pkg_check_modules(GSL REQUIRED gsl)
pkg_check_modules(FFTW3 REQUIRED fftw3 >= 3.3.2)
pkg_check_modules(UDEV udev)
Если настройка проекта завершается с ошибкой, то нужно установить пакеты:
sudo apt-get install pkg-config libgsl-dev libfftw3-dev
Поиск с помощью функции find_package
Если системная библиотека поставляется без файла описания для pkg-config
или необходимо произвести более сложный поиск, например, включающий поиск
исполняемого файла, то может быть написан специальный модуль для CMake
,
который вызывается функцией find_package
. Примеры вызова функции:
# Поиск с помощью функции find_package
find_package(LibXml2)
find_package(CURL REQUIRED)
find_package(Boost 1.55.0 REQUIRED)
Если настройка проекта завершается с ошибкой, то нужно установить пакеты:
sudo apt-get install curl libcurl-dev libboost-all-dev
Автоматически генерируемый заголовочный файл
На этапе конфигурирования проекта можно сгенерировать файл, в который
будут записаны собранные значения параметров. В библиотеке CMLib
присутствует функция cmlib_generate_private_config_hpp()
, создающая файл
${CMAKE_BINARY_DIR}/include/cmlib_private_config.hpp
, в который
записывается информация о имени и версии проекта, дате и типе сборки.
# Автоматически генерируемый заголовочный файл
cmlib_generate_private_config_hpp()
Удаление установленных файлов
В библиотеку CMLib добавлена цель uninstall
, позволяющая удалить файлы,
которые могут быть установлены в результате выполнения цели install
:
cd _build/debug
make install
make uninstall
Архивирование проекта и создание пакетов
Стандартный модуль CPack
предназначен для архивирования исходных
текстов проекта и создания пакетов для установки в целевую систему.
Необходимые переменные устанавливаются в файле cmake/etc/Variables.cmake
см. выше.
Библиотека CMLib предоставляет возможность стандартного разбиения на пакеты в соответствии с критериями, приведёнными в таблице.
Компонент | Имя пакета | Назначение |
---|---|---|
|
|
Основные файла проекта (исполняемые файлы, файлы данных, настроек, ресурсов) |
|
|
Заголовочные файлы, дополнительные файлы необходимые для разработки |
|
|
Статические библиотеки |
|
|
Документация |
Принадлежность устанавливаемого файла к компоненту определяется с помощью
параметра COMPONENT
функции install
. В компонент main
необходимо
помещать файлы для установки на целевую систему (исполняемые файлы,
файлы настроек, файлы данных, разделяемые библиотеки), в компоненты base-dev
и base-libs
--- для установки на систему для разработки (заголовочные
файлы, статические библиотеки). Пример включения устанавливаемых файлов
в компоненты можно посмотреть
здесь.
По умолчанию цель для упаковки исходных текстов называется package_source
,
цель для создания общего архива скомпилированного проекта --- package
,
цель для создания пакетов в формате Debian --- deb
.
Для примера работы с архивированием можно сделать копию репозитория библиотеки MyXLib:
git clone --recursive https://git.246060.ru/f1x1t/myxlib
Дальнейшие шаги:
cd myxlib (1)
mkdir -p _build/debug (2)
cd _build/debug (3)
cmake ../.. -DMYXLIB_BUILD_LIBRARIES=ON -DMYXLIB_BUILD_EXAMPLES=ON (4)
make -j4 (5)
make doc-doxygen (6)
make package (7)
make package_source (8)
make deb (9)
1 | Перейти в каталог проекта |
2 | Создать каталог для сборки проекта |
3 | Перейти в каталог для сборки проекта |
4 | Создать конфигурацию для сборки проекта |
5 | Скомпилировать проект |
6 | Сгенерировать документацию |
7 | Создать архив с результатами компиляции myx_amd64_0.4.0.tar.xz |
8 | Создать архив с исходными текстами myx-0.4.0.tar.xz |
9 | Создать пакеты в формате Debian |
Примеры библиотек и приложений
Базовая библиотека
Проект с базовой библиотекой реализован на основе базового проекта. Исходные тексты содержат комментарии, объясняющие назначение используемых функций. Проект можно посмотреть здесь или сделать его копию командой:
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-library
В файл CMakeLists.txt
, находящийся в корневом каталоге проекта, нужно добавить:
# Поиск библиотеки Boost
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED OFF)
set(Boost_USE_STATIC_RUNTIME ON)
find_package(Boost 1.55.0 REQUIRED)
# Автоматически генерируемый заголовочный файл
cmlib_generate_private_config_hpp()
# Каталог с исходными текстами библиотеки
add_subdirectory(src/cmlib-example)
# Документация
add_breathe_target(doc-breathe)
add_doxygen_target(doc-doxygen LATEX YES HTML YES)
# Создание вспомогательных символических ссылок
add_dependencies(cmlib-example create_auxilary_symlinks)
В подкаталоге src/cmlib-example
нужно создать файл CMakeLists.txt
:
# Название основной цели и имя библиотеки в текущем каталоге
set(TRGT cmlib-example)
# Список файлов исходных текстов
set(TRGT_cpp ${CMAKE_CURRENT_SOURCE_DIR}/init.cpp)
# Список заголовочных файлов (используется для установки)
set(TRGT_hpp ${CMAKE_CURRENT_SOURCE_DIR}/init.hpp)
# Функция для создания цели, результатом которой будет сборка библиотеки
add_common_library(${TRGT} SOURCES ${TRGT_cpp} ${TRGT_hpp})
common_target_properties(${TRGT})
# Добавление к пути поиска заголовочных файлов
target_include_directories(${TRGT} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
# Цель, используемая только для установки
# заголовочных файлов без компиляции проекта
add_custom_target(${TRGT}-install-headers COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=DEV -P
"${CMAKE_BINARY_DIR}/cmake_install.cmake")
# Установка статической библиотеки
install(TARGETS ${TRGT}_static COMPONENT DEV ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
# Установка динамической библиотеки
if(BUILD_SHARED_LIBS)
install(TARGETS ${TRGT}_shared COMPONENT DEV LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
# Установка заголовочных файлов
install(FILES ${TRGT_hpp} COMPONENT DEV DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${TRGT})
# Установка файла для pkg-config
install(FILES ${CMAKE_BINARY_DIR}/${TRGT}.pc COMPONENT DEV DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
файл init.hpp
:
#ifndef CMLIB_EXAMPLE_HPP_
#define CMLIB_EXAMPLE_HPP_
#include <stdint.h>
int32_t cmlib_example_init(int32_t i);
#endif // CMLIB_EXAMPLE_HPP_
и файл init.cpp
:
#include "init.hpp"
#include <boost/range/counting_range.hpp>
int32_t cmlib_example_init(int32_t i = 0)
{
int32_t s = 0;
for ( auto r : boost::counting_range( 1, i ) )
{
s += r;
}
return s;
}
Базовое приложение
Проект с базовым приложением реализован на основе базового проекта. Исходные тексты содержат комментарии, объясняющие назначение используемых функций. Проект можно посмотреть здесь или сделать его копию командой:
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-app
В файл CMakeLists.txt
, находящийся в корневом каталоге проекта, нужно добавить:
# Boost
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED OFF)
set(Boost_USE_STATIC_RUNTIME ON)
find_package(Boost 1.55.0 REQUIRED)
# Автоматически генерируемый заголовочный файл
cmlib_generate_private_config_hpp()
# Приложение
add_subdirectory(src/cmlib-example)
# Документация
add_breathe_target(doc-breathe)
add_doxygen_target(doc-doxygen LATEX YES HTML YES)
# Создание вспомогательных символических ссылок
add_dependencies(cmlib-example create_auxilary_symlinks)
В подкаталоге src/cmlib-example
нужно создать файл CMakeLists.txt
:
# Название основной цели и имя библиотеки в текущем каталоге
set(TRGT cmlib-example)
# Список файлов исходных текстов
set(TRGT_cpp ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
# Функция для создания цели, результатом которой будет сборка приложения
add_executable(${TRGT} ${TRGT_cpp})
common_target_properties(${TRGT})
# Добавление к пути поиска заголовочных файлов
target_include_directories(${TRGT} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
# Имя целевого каталога и выходного файла для цели
set_target_properties(${TRGT} PROPERTIES OUTPUT_NAME ${TRGT})
# Правила для установки
install(TARGETS ${TRGT} COMPONENT MAIN RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
и файл main.cpp
:
#include "compiler_features.hpp"
#include "cmlib_private_config.hpp"
#include <iostream>
#include <boost/range/counting_range.hpp>
int32_t nsum(int32_t i = 0)
{
int32_t s = 0;
for ( auto r : boost::counting_range( 1, i ) )
{
s += r;
}
return s;
}
int main(int argc, char* argv[])
{
// Значение из compiler_features.hpp
std::cout << CMLIB_EXAMPLE_APP_COMPILER_VERSION_MAJOR << std::endl;
// Значение из cmlib_private_config.hpp
std::cout << CMLIB_BUILD_TYPE << std::endl;
// Значение из cmlib_private_config.hpp
std::cout << CMLIB_BUILD_DATE << std::endl;
auto s = nsum( argc );
std::cout << s << std::endl;
return ( s );
}
Подключение внешнего проекта
Проект, использующий для сборки внешний проект, реализован на основе проектов базовой библиотеки и базового приложения. Исходные тексты содержат комментарии, объясняющие назначение используемых функций. Проект можно посмотреть здесь или сделать его копию командой:
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-app-ext
Для подключения проекта базовой библиотеки нужно выполнить:
git submodule add https://git.246060.ru/f1x1t/cmlib-example-library thirdparty/cmlib-example-library
git submodule update --init --recursive
В файл CMakeLists.txt
, находящийся в корневом каталоге проекта, перед функциями
add_subdirectories
нужно добавить:
# Подключение внешних проектов
include(ExternalProject)
ExternalProject_Add(ext-lib
EXCLUDE_FROM_ALL TRUE
SOURCE_DIR ${CMAKE_SOURCE_DIR}/thirdparty/cmlib-example-library
INSTALL_DIR ${CMAKE_BINARY_DIR}
DOWNLOAD_COMMAND ""
BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libcmlib-example.a
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_BUILD_TYPE=Release
)
В результате будет создана цель ext-lib
, являющаяся результатом сборки
подключённой библиотеки. Все функции ExternalProject_Add
необходимо
располагать перед функциям add_subdirectories
, чтобы в указанных подкаталогах
можно было использовать добавленные цели для определения зависимостей.
В файле src/cmlib-example/CMakeLists.txt
после создания цели ${TRGT}
нужно
подключить внешний проект ext-lib
:
# Зависимость от библиотеки из внешнего проекта проекта
add_dependencies(${TRGT} ext-lib)
# Добавление каталога, в который устанавливаются заголовочные файлы
# от внешнего проекта, к списку путей для поиска
target_include_directories(${TRGT} PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>)
# Компоновка с библиотекой из внешнего проекта
target_link_libraries(${TRGT} ${CMAKE_BINARY_DIR}/lib/libcmlib-example.a)
Для проверки работоспособности в файле src/cmlib-example/main.cpp
нужно
вызвать функцию cmlib_example_init
из библиотеки, предоставляемой внешним
проектом. Например, можно заменить его содержимое на:
#include <cmlib-example/init.hpp>
#include <iostream>
int main(int argc, char* argv[])
{
auto s = cmlib_example_init( argc );
std::cout << s << std::endl;
return ( s );
}
Qt5
В данном разделе будут приведены примеры создания консольного и графического
приложений, а также подключения локализации, вызовы препроцессоров moc
,
uic
и rcc
.
Консольное приложение и локализация
Пример консольного приложения на Qt5 с поддержкой локализации основан на проекте базового приложения и библиотеке MyXLib. Исходные тексты содержат комментарии, объясняющие назначение используемых функций. Проект можно посмотреть здесь или сделать его копию командой:
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-app-qt5-con
Для подключения проекта библиотеки MyXLib нужно выполнить:
git submodule add https://git.246060.ru/f1x1t/myxlib thirdparty/myxlib
git submodule update --init --recursive
В файлах CMakeLists.txt
и src/cmlib-example/CMakeLists.txt
нужно
заменить все строки cmlib-example
на cmlib-example-app-qt5-con
.
В файл CMakeLists.txt
, находящийся в корневом каталоге проекта,
перед функциями add_subdirectories
нужно добавить:
# Подключение внешних проектов
include(ExternalProject)
ExternalProject_Add(
myxlib
SOURCE_DIR ${CMAKE_SOURCE_DIR}/thirdparty/myxlib
INSTALL_DIR ${CMAKE_BINARY_DIR}
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND
${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Debug
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} <SOURCE_DIR>
BUILD_COMMAND true)
В результате будет создана цель myxlib
, являющаяся результатом сборки
подключённой библиотеки. Все функции ExternalProject_Add
необходимо
располагать перед функциям add_subdirectories
, чтобы в указанных подкаталогах
можно было использовать добавленные цели для определения зависимостей.
В файле src/cmlib-example/CMakeLists.txt
после создания цели ${TRGT}
нужно
подключить внешний проект myxlib
:
# Зависимость от библиотеки из внешнего проекта проекта
add_dependencies(${TRGT} myxlib)
# Добавление каталога, в который устанавливаются заголовочные файлы
# от внешнего проекта, к списку путей для поиска
target_include_directories(${TRGT} PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>)
Для поиска необходимых компонентов Qt5 нужно в файле CMakeLists.txt
,
находящемся в корневом каталоге проекта, перед вызовом функции
cmlib_generate_private_config_hpp()
добавить строку:
# Используемые компоненты Qt5
find_package(Qt5 COMPONENTS Core REQUIRED)
Если в проекте используются приватные заголовочные файлы из пакета
qtbase5-private-dev
, то нужно добавить следующую инструкцию:
find_package(Qt5Core COMPONENTS Private REQUIRED)
find_package(Qt5 COMPONENTS Core REQUIRED)
В файл src/cmlib-example/CMakeLists.txt
перед вызовом функции
add_executable
добавить строки:
# Правила для создания файла ресурсов с вложенными файлами переводов
qt5_translation(
TRGT_qrc
OUTPUT_DIR ${CMAKE_SOURCE_DIR}/l10n BASE_NAME ${TRGT}
SOURCES ${TRGT_cpp} LANGUAGES ru_RU)
# Путь поиска библиотек созданных при компиляции проекта,
# включая библиотеки из подключённых внешних проектов, например MyXLib
# Функция link_directories обязательно должна находиться перед
# функцией add_executable, иначе компоновка не может быть выполнена
link_directories(${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
В вызове add_executable
подключить использование файла ресурсов
с переводами:
add_executable(${TRGT} ${TRGT_cpp} ${TRGT_qrc})
После чего добавить подключение Qt5 и MyXLib:
# Qt5: подключение заголовочных файлов
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Core_INCLUDE_DIRS})
# Qt5: подключение библиотек
target_link_libraries(${TRGT} Qt5::Core)
# Добавление к пути поиска заголовочных файлов
target_include_directories(${TRGT} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
# Зависимость от библиотеки из внешнего проекта проекта
add_dependencies(${TRGT} myxlib)
# Добавление каталога, в который устанавливаются заголовочные файлы
# от внешнего проекта, к списку путей для поиска
target_include_directories(${TRGT} PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>)
Для проверки работоспособности подключения Qt5 файл
src/cmlib-example/main.cpp
нужно заменить на:
#include "cmlib_private_config.hpp"
#include <myx/qt/translators.hpp>
#include <QCoreApplication>
#include <QDebug>
namespace MQ = myx::qt;
int main( int argc, char** argv )
{
QCoreApplication app( argc, argv );
MQ::QTranslatorsList tl;
qDebug() << QObject::tr( "No" );
MQ::append_translators( tl, QStringLiteral( CMLIB_PROJECT_NAME ) );
qDebug() << QObject::tr( "Yes" );
return( 0 );
}
Для сбора списка строк из файлов исходных кодов и описаний интерфейса,
подлежащих переводу, создаётся цель l10n
. В результате выполнения
в каталоге сборки команды make l10n
в каталоге l10n
, находящемся
в корне проекта, появится файл cmlib-example-app-qt5-con_ru_RU.ts
,
в котором нужно отредактировать переводы с помощью программы linguist
.
После сохранения файла переводов проект нужно пересобрать, файл
переводов в скомпилированном виде будет встроен в исполняемый файл
cmlib-example-app-qt5-con
, а доступ к нему будет осуществляться
с помощью кода:
MQ::QTranslatorsList tl;
MQ::append_translators( tl, QStringLiteral( CMLIB_PROJECT_NAME ) );
Графическое приложение, файлы описания ресурсов и интерфейсов
Пример приложения на Qt5 с использованием графического интерфейса основан на проекте консольного приложения для Qt5. Исходные тексты содержат комментарии, объясняющие назначение используемых функций. Проект можно посмотреть здесь или сделать его копию командой:
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-app-qt5-gui
В каталоге files/share
создать файл описания включаемых ресурсов icon.qrc
:
<RCC>
<qresource prefix="/icon">
<file alias="icon.png">icon.png</file>
</qresource>
</RCC>
и загрузить файл иконки:
wget https://git.246060.ru/f1x1t/cmlib-example-app-qt5-gui/raw/branch/master/files/share/icon.png
Для графического приложения нужно создать файл описания интерфейса
src/cmlib-example/test_window.ui
:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TestWindow</class>
<widget class="QMainWindow" name="TestWindow">
<property name="geometry">
<rect><x>0</x><y>0</y><width>413</width><height>253</height></rect>
</property>
<property name="windowTitle">
<string>Test Window</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QPushButton" name="exitButton">
<property name="geometry">
<rect><x>170</x><y>30</y><width>80</width><height>26</height></rect>
</property>
<property name="text">
<string>Press me</string>
</property>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>
заголовочный файл src/cmlib-example/test_window.hpp
:
#ifndef TEST_WINDOW_HPP_
#define TEST_WINDOW_HPP_
#pragma once
#include "ui_test_window.h"
#include <QMainWindow>
class TestWindow : public QMainWindow, private Ui::TestWindow {
Q_OBJECT
public:
TestWindow(QMainWindow *parent = nullptr);
virtual ~TestWindow();
};
#endif /* TEST_WINDOW_HPP_ */
и файл с реализацией конструктора, в котором проводится инициализация
графических элементов, src/cmlib-example/test_window.cpp
:
#include "test_window.hpp"
TestWindow::TestWindow(QMainWindow* parent) :
QMainWindow(parent),
Ui::TestWindow()
{
setupUi(this);
}
TestWindow::~TestWindow() = default;
Для отображения графического окна нужно заменить файл
src/cmlib-examples/main.cpp
на:
#include "cmlib_private_config.hpp"
#include "test_window.hpp"
#include <myx/qt/translators.hpp>
#include <QApplication>
#include <QIcon>
#include <QDebug>
namespace MQ = myx::qt;
int main( int argc, char** argv )
{
QApplication app( argc, argv );
qDebug() << QObject::tr( "No" );
// Подключение переводов
MQ::QTranslatorsList tl;
MQ::append_translators( tl, QStringLiteral( CMLIB_PROJECT_NAME ) );
qDebug() << QObject::tr( "Yes" );
// Установка иконки для программы
QApplication::setWindowIcon( QIcon( ":/icon/icon.png" ) );
// Создание и отображение главного окна
auto* w = new TestWindow();
w->show();
return( QApplication::exec() );
}
В файлах CMakeLists.txt
и src/cmlib-example/CMakeLists.txt
нужно
заменить все строки cmlib-example-app-qt5-con
на cmlib-example-app-qt5-gui
.
Для поиска необходимых компонентов Qt5 нужно в файле CMakeLists.txt
,
находящемся в корневом каталоге проекта, добавить поиск компонентов
Gui
и Widgets
:
# Используемые компоненты Qt5
find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
В файле src/cmake-example/CMakeLists.txt
добавить новые файлы к списку
файлов, используемых для компиляции:
###
# Списки файлов проекта
###
# Исходные коды
set(TRGT_cpp
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_window.cpp)
# Заголовочные файлы, для которых необходима обработка препроцессором moc
# (содержат класс, унаследованный от QObject, использующий сигналы и/или слоты)
set(TRGT_moc_hpp
${CMAKE_CURRENT_SOURCE_DIR}/test_window.hpp)
# Другие заголовочные файлы
set(TRGT_hpp)
# Файлы с описанием графического интерфейса для Qt
set(TRGT_ui
${CMAKE_CURRENT_SOURCE_DIR}/test_window.ui)
# Файлы описания ресурсов, включаемых в исполняемый файл
set(TRGT_qrc
${CMAKE_SOURCE_DIR}/files/share/icon.qrc)
###
# Конец списков файлов
###
Для обеспечения работы препроцессоров Qt необходимо создать правила преобразования файлов:
# Правило для автоматической генерации препроцессором uic
qt5_wrap_ui(TRGT_ui_h ${TRGT_ui})
# Правило для автоматической генерации препроцессором moc
qt5_wrap_cpp(TRGT_moc_cpp ${TRGT_moc_hpp})
# Правила для создания файла ресурсов с вложенными файлами переводов
qt5_translation(
TRGT_qrc_cpp
OUTPUT_DIR ${CMAKE_SOURCE_DIR}/l10n BASE_NAME ${TRGT}
SOURCES ${TRGT_cpp} ${TRGT_ui} LANGUAGES ru_RU)
# Правило для автоматической генерации препроцессором qrc
# (обязательно после вызова функции qt5_translation, если она есть,
# так как она добавляет свои файлы к списку ресурсов)
qt5_add_resources(TRGT_qrc_cpp ${TRGT_qrc})
Цель для создания исполняемого файла нужно изменить таким образом, чтобы она зависела от файлов с исходными кодами и файлов, генерируемых препроцессорами:
add_executable(${TRGT} ${TRGT_ui_h} ${TRGT_moc_cpp} ${TRGT_qrc_cpp} ${TRGT_cpp} ${TRGT_hpp})
Подключение заголовочных файлов и библиотек Qt должно выглядеть так:
# Qt5: подключение заголовочных файлов
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Core_INCLUDE_DIRS})
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Gui_INCLUDE_DIRS})
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Widgets_INCLUDE_DIRS})
# Qt5: подключение библиотек
target_link_libraries(${TRGT} Qt5::Core Qt5::Gui Qt5::Widgets)
В результате выполнения в каталоге сборки команды make l10n
в каталоге l10n
, находящемся в корне проекта, появится файл
cmlib-example-app-qt5-gui_ru_RU.ts
,
в котором нужно отредактировать переводы с помощью программы linguist
.
После сохранения файла переводов проект нужно пересобрать, файл
переводов в скомпилированном виде будет встроен в исполняемый файл.
Дополнительные возможности
Библиотека CMLib содержит шаблонные функции для использования в программных проектах. Пример проекта с примерами использования функций основан на проекте графического приложения для Qt5. Исходные тексты содержат комментарии, объясняющие назначение используемых функций. Проект можно посмотреть здесь или сделать его копию командой:
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-app-features
Форматирование исходных текстов
Функция add_format_sources
генерирует цель для форматирования файлов проекта
в едином стандарте, для её использования требуются установленные программы
dos2unix
и uncrustify
. Утилита dos2unix
приводит переводы строк в
файлах к стандарту, принятому в Unix. Утилита uncrustify
форматирует
файлы с исходными кодами на языке C++ в соответствии с правилами,
перечисленными в файле cmake/etc/uncrustify/default.cfg
. Можно использовать
собственный файл default.cfg
или подключить подмодуль из репозитория:
git submodule add https://git.246060.ru/f1x1t/uncrustify-config.git cmake/etc/uncrustify
git commit -m "Настройка uncrustify"
Настройка правил форматирования помогает другим разработчикам придерживаться вашего стиля программирования и отправлять изменения в ваш проект в формате, который удобен вам. Проявите заботу о своих коллегах и своём проекте! |
Пример использования:
# Создание цели format-sources для автоматического форматирования кода
add_format_sources(${TRGT} ${TRGT_cpp} ${TRGT_headers})
Для автоматической проверки исходных текстов на соответствие стандарту
форматирования можно к локальному репозиторию подключить скрипт, выполняемый
перед фиксацией (pre-commit
):
wget -O - https://git.246060.ru/f1x1t/githooks/archive/master.tar.gz | tar zx --strip-components=1 -C .git/hooks
Статический анализ исходных кодов
Для работы с программами на языке C++ используются утилиты, выполняющие статический анализ кода и генерирующие отчёты, помогающие программисту находить и устранять ошибки. Эти программы применяют методы, позволяющие в синтаксически корректном коде находить недостатки или ошибки, которые пропускает компилятор, ценой продолжительного анализа исходных текстов.
Библиотека CMLib поддерживает анализаторы clazy, Clang Tidy, Clang Static Analyzer и PVS-Studio.
clazy
Функция add_clazy_check
создаёт цели, которые используются
для проверки исходных текстов анализатором clang
.
Пример использования:
# Создание цели clazy-check для проверки утилитой clazy
add_clazy_check(${TRGT} ${TRGT_cpp} ${TRGT_hpp} ${TRGT_moc_hpp})
Clang Tidy
Функция add_clang_tidy_check
создаёт цели, которые используются
для проверки исходных текстов анализатором clang-tidy
.
Правила проверок задаются в файле .clang-tidy
, находящемся в
корневом каталоге проекта. Пример использования:
# Создание цели clang-tidy-check для проверки утилитой clang-tidy
add_clang_tidy_check(${TRGT} ${TRGT_cpp} ${TRGT_hpp} ${TRGT_moc_hpp})
Clang Static Analyzer
Функция add_clang_analyze_check
создаёт цели, которые используются
для проверки исходных текстов анализатором clang-analyze
.
Пример использования:
# Создание цели clang-analyze-check для проверки утилитой clang-analyze
add_clang_analyze_check(${TRGT} ${TRGT_cpp} ${TRGT_hpp} ${TRGT_moc_hpp})
PVS-Studio
Функция add_pvs_check
создаёт цели, которые используются
для проверки исходных текстов анализатором pvs-studio-analyzer
.
Пример использования:
# Создание цели pvs-check для проверки утилитой pvs-studio-analyzer
add_pvs_check(${TRGT})
Автоматическое исправление кода
Редактирование кода в автоматическом режиме может приводить к его неработоспособности, хотя это и маловероятно. Перед выполнением действий, приведённых в данном раздела, желательно фиксировать текущее состояние в репозитории или делать резервную копию. |
clazy
Программа clazy может преобразовывать в программах, использующих Qt, подключения сигналов и слотов старого типа, производить замену старых ключевых слов, подставлять оптимизированные способы для инициализации строк, исправлять циклы и передачу аргументов в функции для избежания лишних копирований. Для использования данной возможности необходимо установить пакеты:
sudo apt-get install clazy clang-tools
Для включения автоматического исправления нужно в настройках сборки проекта
clazy-check
:
Затем в перечне опций включить CMLIB_CLAZY_FIX
, нажать кнопку
Применить изменения, а затем скомпилировать проект Ctrl+B:
Пример проекта, в котором показаны возможности clazy, можно посмотреть здесь. Содержание изменений, произведённых автоматически, можно увидеть здесь.
Можно сделать копию репозитория и выполнить правки в автоматическом режиме самостоятельно:
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-clazy-fix
Clang-Tidy
Анализатор Clang-Tidy предоставляет более широкие возможности по автоматической правке кода. В проектах, использующих Qt, желательно использовать Clang-Tidy после clazy. Программу можно установить командой:
sudo apt-get install clang-tools clang-tidy
Для включения автоматического исправления нужно в настройках сборки проекта
clang-tidy-check
:
Затем в перечне опций включить CMLIB_CLANG_TIDY_FIX
, нажать кнопку
Применить изменения, а затем скомпилировать проект Ctrl+B:
Динамический анализ программы
Динамический анализ программы позволяет ценой значительного замедления скорости работы получить дополнительную информацию о ходе её выполнения. Современные компиляторы делают вставку инструкций в определённые точки программы, во время работы программы в них собирается необходимая информация, а по её завершению предоставляется отчёт. Основная информация о работе таких анализаторов находится здесь.
Для обеспечения возможности подключения динамического анализа к проекту нужно выполнить функцию (обязательно после подключения всех библиотек):
# Подключение настроек для динамического анализа программы
add_sanitizers(${TRGT})
Подключение анализатора осуществляется включением опций при запуске CMake для генерации сборочных файлов. Некоторые из опций между собой несовместимы, в случае попытки совместного использования будет выведено сообщение об ошибке.
Опция | Назначение |
---|---|
|
Определение ошибок при работе с памятью: использование после освобождения, использование за пределами области видимости, переполнения буферов в стеке, на куче, в общей памяти, утечки памяти, нарушение порядка инициализации |
|
Определение нарушений путей исполнения инструкций программы |
|
Определение утечек памяти |
|
Статическая компоновка анализатора с программой |
|
Определение попыток доступа к неинициализированным областям памяти |
|
Определение переполнения буфера стека |
|
Определение состояние гонок |
|
Определение невыровненных и нулевых указателей, переполнения знаковых целых, преобразования типов с плавающей точкой, ведущих к переполнению результирующей переменной |
Анализ покрытия кода
Для сбора информации о точном количестве исполнений для каждого оператора в программе используется программа Gcov, входящая в состав компилятора GCC.
Для обеспечения возможности подключения анализа покрытия кода к проекту нужно выполнить функцию (обязательно после подключения всех библиотек):
# Подключение возможности использования утилиты Gcov
# для исследования покрытия кода
add_code_coverage(${TRGT})
Подключение осуществляется включением опции ENABLE_CODE_COVERAGE
при запуске CMake для генерации сборочных файлов. В результате будут
созданы две дополнительные цели coverage-${TRGT}
для сбора статистики
после работы программы и coverage-report-${TRGT}
для её вывода
в виде HTML-страниц.
Пример анализа покрытия кода на примере проекта cmlib-example-app-features
:
mkdir -p _build/debug
cd _build/debug
cmake ../.. -DENABLE_CODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug
make
bin/cmlib-example-app-features
make coverage-cmlib-example-app-features
make coverage-report-cmlib-example-app-features
После выполнения этих команд в каталоге report-cmlib-example-app-features
будет сформирован отчёт в виде HTML-страниц.
Профилирование кода
Библиотека CMLib предоставляет вариант сборки для профилирования кода,
для которого можно сгенерировать сборочные файлы, присвоив переменной
CMAKE_BUILD_TYPE
значение Profile
:
mkdir -p _build/profile
cd _build/profile
cmake ../.. -DCMAKE_BUILD_TYPE=Profile
По окончании работы исполняемого файла будет сгенерирован файл
gmon.out
, по данным из которого можно строить отчёты утилитой gprof
.
Например:
./cmlib-example-app-features
gprof -b cmlib-example-app-features gmon.out > analysis-tree.txt
gprof -b -p cmlib-example-app-features gmon.out > analysis-flat.txt
Ускорение компиляции
Для ускорения компиляции используется сторонний модуль cotire, который автоматизирует использование предварительно откомпилированных заголовков и организует пакетный режим обработки исходных файлов в генератора. Аналогичные функции встроены в CMake, начиная с версии 3.16.
Для обеспечения возможностей, предоставляемых модулем cotire, нужно выполнить функцию (обязательно после подключения всех библиотек):
# Подключение возможности включения пакетного режима обработки
# исходных файлов в генераторах для ускорения сборки
cotire(${TRGT})
В результате будут созданы цели с суффиксом _unity
, при сборки
которых будут применяться приведённые выше методы ускорения.
Пример использования cotire для ускорения сборки
на примере проекта cmlib-example-app-features
:
mkdir -p _build/debug
cd _build/debug
cmake ../..
make all_unity
Документирование кода
Для документирования кода используются блоки комментариев, оформленные для обработки программой Doxygen. Установка программы:
sudo apt-get install doxygen
Пример комментария:
/**
* @brief Базовый класс
*/
class Base {
public:
/**
* @brief Конструктор
*/
Base();
/**
* @brief Деструктор
*/
~Base();
/**
* @brief Вычисление квадратного корня
* @param value Входное значение
* @return Квадратный корень от value
*/
double sqrt(double value);
};
Поддержка автоматической генерации документации реализована
в функциях библиотеки CMLib add_doxygen_target
и
add_breathe_target
, которые необходимо вызвать в основном
файле CMakeLists.txt
проекта.
# Документация
add_doxygen_target(doc-doxygen LATEX YES HTML YES)
add_breathe_target(doc-breathe)
В результате будут добавлены цели doc-doxygen
и doc-breathe
,
которые можно использовать после конфигурирования проекта:
make doc-doxygen
make doc-breathe
Шаблоны для комментирования файлов, классов и функций
можно автоматически расставить в файлах исходных кодов
исполнением цели doc-add-comments
при наличии установленной
программы uncrustify
:
make doc-add-comments