dsp-site/wiki/Prog/Development/CMake управление проектом.adoc
2020-04-11 18:07:10 +03:00

927 lines
35 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

= CMake: управление проектом
:title-separator: {sp}|
:category: Программирование
:tags: программирование, cmake,
:toc:
== Полезные ссылки
* https://github.com/onqtam/awesome-cmake[Каталог ссылок]
* https://cgold.readthedocs.io/en/latest/index.html[CGold: The Hitchhikers Guide to the CMake]
== Структура каталогов проекта
Файлы проекта и результаты компиляции размещаются в каталогах:
....
└── project
├── _build
│ ├── debug
│ │ ├── bin
│ │ ├── etc
│ │ ├── files
│ │ │ ├── data
│ │ │ ├── lib
│ │ │ └── log
│ │ ├── include
│ │ └── lib
│ └── release
├── .git
├── .gitlab-ci
├── cmake
│ ├── cmlib
│ ├── doc
│ ├── etc
│ │ └── uncrustify
│ ├── find
│ └── generators
├── doc
├── files
│ ├── data
│ ├── etc
│ ├── lib
│ └── log
├── l10n
├── src
│ ├── app
│ └── lib
├── thirdparty
└── tools
....
Назначение каталогов приведено в таблице.
[cols="2,4",options="header",]
|===
|Каталог | Назначение
|`_build` | Результаты компиляции
|`_build/debug` | Результаты компиляции в режиме отладки
|`_build/debug/bin` | Исполняемые файлы
|`_build/debug/etc` | Символическая ссылка на каталог `cmex/files/etc`
|`_build/debug/files/data` | Символическая ссылка на каталог `cmex/files/data`
|`_build/debug/files/lib` | Символическая ссылка на каталог `cmex/files/lib`
|`_build/debug/files/log` | Символическая ссылка на каталог `cmex/files/log`
|`_build/debug/include` | Заголовочные файлы копируемые и генерируемые во время сборки
|`_build/debug/lib` | Статические и динамические библиотеки
|`_build/release` | Результаты компиляции в режиме выпуска (иерархия аналогична `debug`)
|`.git` | Системные файлы репозитория git
|`.gitlab.ci` | Шаблон правил для автоматической сборки на сервере Gitlab
|`cmake` | Файлы с дополнительными функциями для CMake
|`cmake/cmlib` | Библиотека функций для CMake
|`cmake/doc` | Правила для автоматической генерации документации
|`cmake/etc` | Файлы настроек, используемые в CMake
|`cmake/etc/uncrustify` | Файл настройки для программы автоматического форматирования исходных текстов
|`cmake/find` | Модули CMake для поиска внешних программ и библиотек
|`cmake/generators` | Генераторы проектов
|`doc` | Документация для проекта
|`files` | Каталог для дополнительных файлов
|`files/etc` | Каталог для файлов настроек проекта
|`files/data` | Каталог для неизменяемых файлов
|`files/lib` | Каталог для изменяемых файлов
|`files/log` | Каталог для журналов
|`l10n` | Файлы переводов
|`src` | Исходные тексты
|`src/app` | Исходные тексты программы
|`src/lib` | Исходные тексты библиотеки
|`thirdparty` | Исходные тексты дополнительных и сторонних проектов
|`tools` | Дополнительные утилиты
|===
Каталог `_build` создаётся, чтобы избежать попадания создаваемых во время
сборки файлов в иерархию основного проекта. Запись результатов сборки
проекта внутрь иерархии каталогов с исходными текстами приводит к
засорению формируемыми на этапе сборки файлами, которые затрудняют
разработку, поиск в оригинальных файлах и мешают ориентироваться в
проекте. При работе с несколькими типами сборки, например, отладка и
выпуск, появляется необходимость корректного полного удаления
результатов предыдущего тип сборки.
[[base-project]]
== Базовый проект
Проект, в котором выполнены приведённые в данном разделе действия,
можно посмотреть https://git.246060.ru/f1x1t/cmlib-example-base[здесь]
или сделать его копию командой:
[source,sh]
----
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-base
----
=== Инициализация подмодулей
Для начала нужно создать каталог для проекта, перейти в него и
инициализировать репозиторий git:
[source,sh]
----
mkdir cmlib-example-base
cd cmlib-example-base
git init
----
Для подключения основных подмодулей, содержащих дополнительные функции
для работы с проектом, и фиксации произведённого изменения нужно выполнить:
[source,sh]
----
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 submodule add https://git.246060.ru/f1x1t/cmake-doc.git cmake/doc
git submodule add https://git.246060.ru/f1x1t/uncrustify-config.git cmake/etc/uncrustify
git commit -a -m "Начало проекта"
----
Отправить изменения в проекте на сервер и сделать ветку `master` основной
(можно пропустить):
[source,sh]
----
git remote add origin АДРЕС_РЕПОЗИТОРИЯ_НА_СЕРВЕРЕ
git push -u origin master
----
Загрузить шаблоны для автоматической сборки проекта в разных вариантах
программных окружений и зафиксировать изменения:
[source,sh]
----
mkdir .gitlab-ci
wget -O .gitlab-ci/scheduled.yml https://git.246060.ru/f1x1t/gitlab-ci/raw/branch/master/.gitlab-ci/scheduled.yml
wget -O .gitlab-ci.yml https://git.246060.ru/f1x1t/gitlab-ci/raw/branch/master/.gitlab-ci.yml
git add .gitlab-ci.yml .gitlab-ci/scheduled.yml
git commit -m "Настройка автосборки"
----
Загрузить файл настройки для анализатора Clang-Tidy:
[source,sh]
----
wget https://git.246060.ru/f1x1t/clang-tidy-config/raw/branch/master/.clang-tidy
git add .clang-tidy
git commit -m "Настройка Clang-Tidy"
----
Создать стандартные файлы и каталоги:
[source,sh]
----
mkdir -p doc/breathe
touch doc/breathe/index.md.in
mkdir -p files/etc
touch files/etc/.keep-directory
mkdir -p files/data
touch files/data/.keep-directory
mkdir -p files/lib
touch files/lib/.keep-directory
mkdir -p files/log
touch files/log/.keep-directory
git add doc files
git commit -m "Стандартные файлы и каталоги"
----
Создать файл `.gitignore` для исключения каталогов и файлов из-под контроля git:
[source,sh]
----
wget https://git.246060.ru/f1x1t/cmlib-gitignore/raw/branch/master/.gitignore
git add .gitignore
git commit -m "Шаблон для игнорирования каталогов и файлов"
----
=== Базовые инструкции в CMake
В корневом каталоге проекта нужно создать файл `CMakeLists.txt`:
[source,cmake]
----
# Минимальная версия 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)
----
Значение версии следует формировать согласно правилам
https://semver.org/lang/ru/[семантического версионирования].
Для подключения функций для CMake из библиотеки CMLib, нужно добавить
в файл `CMakeLists.txt` строки:
[source,cmake]
----
# В каталоге 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)
----
[[variables-cmake]]
В каталоге `cmake/etc` требуется создать файл `Variables.cmake`,
в котором должны быть определены переменные, используемые
библиотекой CMLib для архивирования исходных текстов, автоматического
создания пакетов, генерации документации:
[source,cmake]
----
set(ORGANIZATION_NAME "org")
set(AUTHOR_NAME "John Doe")
set(DOXYGEN_PROJECT_TITLE "Пример проекта (начало)")
set(DOXYGEN_GENERATE_LATEX YES)
set(DOXYGEN_GENERATE_HTML YES)
set(CPACK_GENERATOR "TXZ;DEB")
set(CPACK_PACKAGE_CONTACT "John Doe <box@mail.domain>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "CMake project example")
set(CPACK_DEBIAN_PACKAGE_SECTION "misc")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_SOURCE_IGNORE_FILES
"${CMAKE_BINARY_DIR}"
"/\\\\.git/"
"/\\\\.gitlab-ci/"
"^${CMAKE_SOURCE_DIR}/.?build.?/"
"^${CMAKE_SOURCE_DIR}/.?output.?/"
"^${CMAKE_SOURCE_DIR}/files/lib"
"^${CMAKE_SOURCE_DIR}/files/log"
"\\\\.clang-tidy$"
"\\\\.cmake-format$"
"\\\\.gitignore$"
"\\\\.gitattributes$"
"\\\\.gitmodules$"
"\\\\.gitlab-ci.yml"
"CMakeLists.txt.user.*"
"~$"
"\\\\.swp$")
----
Произведённые изменения можно зафиксировать:
[source,sh]
----
git add cmake/etc/Variables.cmake CMakeLists.txt
git commit -m "Подключение библиотеки CMLib"
----
Чтобы проверить корректность подключения CMLib, можно выполнить команду:
[source,sh]
----
(mkdir -p _build && cd _build && cmake .. && make && echo OK)
----
Если последней строкой вывода будет `OK`, то настройка завершена верно.
== Поиск системных библиотек
Поиск программ, библиотек и заголовочных файлов, установленных в системе, можно
выполнять с помощью программы https://en.wikipedia.org/wiki/Pkg-config[`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`:
[source,cmake]
----
# Поиск библиотек с помощью pkgconfig
find_package(PkgConfig REQUIRED)
pkg_check_modules(GSL REQUIRED gsl)
pkg_check_modules(FFTW3 REQUIRED fftw3)
pkg_check_modules(UDEV udev)
----
=== Поиск с помощью функции `find_package`
Если системная библиотека поставляется без файла описания для `pkg-config`
или необходимо произвести более сложный поиск, например, включающий поиск
исполняемого файла, то может быть написан специальный модуль для `CMake`,
который вызывается функцией `find_package`. Примеры вызова функции:
[source,cmake]
----
# Поиск с помощью функции find_package
find_package(LibXml2)
find_package(CURL REQUIRED)
----
== Автоматически генерируемый заголовочный файл
На этапе конфигурирования проекта можно сгенерировать файл, в который
будут записаны собранные значения параметров. В библиотеке CMLib
присутствует функция `cmlib_config_hpp_generate()`, создающая файл
`${CMAKE_BINARY_DIR}/include/cmlib_private_config.hpp`, в который
записывается информация о имени и версии проекта, дате и типе сборки.
[source,cmake]
----
# Автоматически генерируемый заголовочный файл
cmlib_config_hpp_generate()
----
== Удаление установленных файлов
В библиотеку CMLib добавлена цель `uninstall`, позволяющая удалить файлы,
которые могут быть установлены в результате выполнения цели `install`:
[source,sh]
----
cd _build/debug
make install
make uninstall
----
== Архивирование проекта и создание пакетов
Стандартный модуль `CPack` предназначен для архивирования исходных
текстов проекта и создания пакетов для установки в целевую систему.
Необходимые переменные устанавливаются в файле `cmake/etc/Variables.cmake`
<<variables-cmake,см. выше>>.
Устанавливаемые файлы делятся на две группы `MAIN` и `DEV` с помощью
параметра `COMPONENT` функции `install`. В группу `MAIN` необходимо
помещать файлы для установки на целевую систему (исполняемые файлы,
файлы настроек, файлы данных, разделяемые библиотеки), а в группу `DEV` ---
для установки на систему для разработки (заголовочные файлы, статические
библиотеки).
По умолчанию цель для упаковки исходных текстов называется `package_source`.
Бинарные пакеты создаются программой `cpack`. Пример:
[source,sh]
----
cd _build/debug
make
make package_source
cpack
----
== Примеры библиотек и приложений
[[base-lib-project]]
=== Базовая библиотека
Проект с базовой библиотекой реализован на основе <<base-project,базового проекта>>.
Исходные тексты содержат комментарии, объясняющие назначение используемых функций.
Проект можно посмотреть https://git.246060.ru/f1x1t/cmlib-example-library[здесь]
или сделать его копию командой:
[source,sh]
----
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-library
----
В файл `CMakeLists.txt`, находящийся в корневом каталоге проекта, нужно добавить:
[source,cmake]
----
# Поиск библиотеки 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 COMPONENTS headers)
# Автоматически генерируемый заголовочный файл
cmlib_config_hpp_generate()
# Каталог с исходными текстами библиотеки
add_subdirectory(src/cmlib-example)
# Документация
add_subdirectory(cmake/doc)
# Создание вспомогательных символических ссылок
add_dependencies(cmlib-example create_auxilary_symlinks)
----
В подкаталоге `src/cmlib-example` нужно создать файл `CMakeLists.txt`:
[source,cmake]
----
# Название основной цели и имя библиотеки в текущем каталоге
set(TRGT cmlib-example)
# Список файлов исходных текстов
set(TRGT_sources ${CMAKE_CURRENT_SOURCE_DIR}/init.cpp)
# Список заголовочных файлов (используется для установки)
set(TRGT_headers ${CMAKE_CURRENT_SOURCE_DIR}/init.hpp)
# Функция для создания цели, результатом которой будет сборка библиотеки
add_common_library(TARGET ${TRGT} SOURCES ${TRGT_sources})
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_headers} 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`:
[source,cpp]
----
#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`:
[source,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;
}
----
[[base-app-project]]
=== Базовое приложение
Проект с базовым приложением реализован на основе <<base-project,базового проекта>>.
Исходные тексты содержат комментарии, объясняющие назначение используемых функций.
Проект можно посмотреть https://git.246060.ru/f1x1t/cmlib-example-app[здесь]
или сделать его копию командой:
[source,sh]
----
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-app
----
В файл `CMakeLists.txt`, находящийся в корневом каталоге проекта, нужно добавить:
[source,cmake]
----
# 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 COMPONENTS headers)
# Автоматически генерируемый заголовочный файл
cmlib_config_hpp_generate()
# Приложение
add_subdirectory(src/cmlib-example)
# Документация
add_subdirectory(cmake/doc)
# Создание вспомогательных символических ссылок
add_dependencies(cmlib-example create_auxilary_symlinks)
----
В подкаталоге `src/cmlib-example` нужно создать файл `CMakeLists.txt`:
[source,cmake]
----
# Название основной цели и имя библиотеки в текущем каталоге
set(TRGT cmlib-example)
# Список файлов исходных текстов
set(TRGT_sources ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
# Функция для создания цели, результатом которой будет сборка приложения
add_executable(${TRGT} ${TRGT_sources})
common_target_properties(${TRGT})
# Добавление к пути поиска заголовочных файлов
target_include_directories(${TRGT} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
# Имя целевого каталога и выходного файла для цели
set_target_properties(${TRGT}
PROPERTIES
OUTPUT_NAME ${TRGT}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}
)
# Правила для установки
install(TARGETS ${TRGT} COMPONENT MAIN RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
----
и файл `main.cpp`:
[source,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 );
}
----
=== Подключение внешнего проекта
Проект, использующий для сборки внешний проект, реализован на основе проектов
<<base-lib-project,базовой библиотеки>> и <<base-app-project,базового приложения>>.
Исходные тексты содержат комментарии, объясняющие назначение используемых функций.
Проект можно посмотреть https://git.246060.ru/f1x1t/cmlib-example-app-ext[здесь]
или сделать его копию командой:
[source,sh]
----
git clone --recursive https://git.246060.ru/f1x1t/cmlib-example-app-ext
----
Для подключения проекта базовой библиотеки нужно выполнить:
[source,sh]
----
git submodule add https://git.246060.ru/f1x1t/cmlib-example-library thirdparty/cmlib-example-library
git submodule update --init --recursive
----
Вызовы этих функций нужно сделать до функций `add_subdirectories`, чтобы
в подключенных подкаталогах можно было использовать цель `cmext` для
определения зависимостей.
В файл `CMakeLists.txt`, находящийся в корневом каталоге проекта, перед функциями
`add_subdirectories` нужно добавить:
[source,cmake]
----
# Подключение внешних проектов
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`:
[source,cmake]
----
# Зависимость от библиотеки из внешнего проекта проекта
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` из библиотеки, предоставляемой внешним
проектом. Например, можно заменить его содержимое на:
[source,cpp]
----
#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
Для поиска необходимых компонентов Qt5 нужно в файле `/CMakeLists.txt`,
находящемся в корневом каталоге проект, перед вызовом функции
`cmlib_config_hpp_generate()` добавить строку:
[source,cmake]
----
find_package(Qt5 COMPONENTS Core Network Gui Widgets Concurrent Sql REQUIRED)
----
Библиотека CMLib автоматически подключает вызов препроцессора `moc` и
компилятора ресурсов `rcc`, если цель использует модуль `Core`, и
вызывает компилятор файлов описания интерфейса, если цель использует
модуль `Widgets`.
== Консольное приложение
В файл `cmex/src/cmex/CMakeLists.txt` добавить строки:
[source,cmake]
----
# Qt5
qt_translation(TARGET ${TRGT} TS_DIR ${CMAKE_SOURCE_DIR}/l10n LANGUAGES ru_RU)
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Core_INCLUDE_DIRS})
target_compile_options(${TRGT} PUBLIC "${Qt5Core_EXECUTABLE_COMPILE_FLAGS}")
target_link_libraries(${TRGT} Qt5::Core)
----
Для проверки работоспособности подключения Qt5 файл
`cmex/src/cmex/main.cpp` нужно заменить на:
[source,cpp]
----
#include "compiler_features.hpp"
#include "cmlib_config.hpp"
#include <QtCore>
#include <iostream>
#include <cmext/cmext.hpp>
#include "cmex.hpp"
QTextStream& qStdOut()
{
static QTextStream ts(stdout);
return ts;
}
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
QTranslator translator;
if (translator.load(QLocale(), "cmex_app", QLatin1String("_"), QLatin1String(":/qm")))
{
app.installTranslator(&translator);
}
// Значение из compiler_features.hpp
qStdOut() << QObject::tr("Compiler version: ") << CMEX_COMPILER_VERSION_MAJOR << endl;
// Значение из cmlib_config.hpp
qStdOut() << QObject::tr("Project version: ") << CMEX_VERSION_STR << endl;
// Значение из cmlib_config.hpp
qStdOut() << QObject::tr("Build type: ") << BUILD_TYPE << endl;
// Функция из внутренней библиотеки
qStdOut() << QObject::tr("libcmex function call: ") << cmex_init(4) << endl;
// Функция из внешней библиотеки
qStdOut() << QObject::tr("libcmext function call: ") << cmext_init(9) << endl;
return 0;
}
----
После сборки проекта в каталоге `cmex/l10n` появится файл
`cmex_app_ru_RU.ts`, в котором нужно отредактировать переводы с помощью
программы `linguist`. После сохранения переводов проект нужно
пересобрать, файл переводов в скомпилированном виде будет встроен в
исполняемый файл `cmex`, а доступ к нему будет осуществляться с помощью
кода:
[source,cpp]
----
if (translator.load(QLocale(), "cmex_app", QLatin1String("_"), QLatin1String(":/qm")))
{
app.installTranslator(&translator);
}
----
== Графическое приложение
Для создания минимального графического приложения нужно создать файл
описания интерфейса `cmex/src/cmex/my_main_window.ui`:
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MyMainWindow</class>
<widget class="QMainWindow" name="MyMainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>678</width>
<height>415</height>
</rect>
</property>
<property name="windowTitle">
<string>Main Window</string>
</property>
<widget class="QWidget" name="centralwidget"/>
</widget>
<resources/>
<connections/>
</ui>
----
заголовочный файл `cmex/src/cmex/my_main_window.hpp`:
[source,cpp]
----
#ifndef CMEX_MY_MAIN_WINDOW_HPP_
#define CMEX_MY_MAIN_WINDOW_HPP_
#include <QWidget>
#include "ui_my_main_window.h"
class MyMainWindow : public QWidget, private Ui::MyMainWindow {
Q_OBJECT
public:
MyMainWindow(QWidget* parent = 0);
virtual ~MyMainWindow();
};
#endif /* CMEX_MY_MAIN_WINDOW_HPP_ */
----
и файл с реализацией конструктора и деструктора
`cmex/src/cmex/my_main_window.cpp`:
[source,cpp]
----
#include "my_main_window.hpp"
MyMainWindow::MyMainWindow(QWidget* parent) {
}
MyMainWindow::~MyMainWindow() {
}
----
Для отображения графического окна нужно заменить файл
`cmex/src/cmex/main.cpp` на:
[source,cpp]
----
#include "compiler_features.hpp"
#include "cmlib_config.hpp"
#include <QtCore>
#include <QtWidgets>
#include <iostream>
#include <cmext/cmext.hpp>
#include "cmex.hpp"
#include "my_main_window.hpp"
QTextStream& qStdOut()
{
static QTextStream ts(stdout);
return ts;
}
int main(int argc, char **argv) {
QApplication app(argc, argv);
QTranslator translator;
if (translator.load(QLocale(), "cmex_app", QLatin1String("_"), QLatin1String(":/qm")))
{
app.installTranslator(&translator);
}
// Значение из compiler_features.hpp
qStdOut() << QObject::tr("Compiler version: ") << CMEX_COMPILER_VERSION_MAJOR << endl;
// Значение из cmlib_config.hpp
qStdOut() << QObject::tr("Project version: ") << CMEX_VERSION_STR << endl;
// Значение из cmlib_config.hpp
qStdOut() << QObject::tr("Build type: ") << BUILD_TYPE << endl;
// Функция из внутренней библиотеки
qStdOut() << QObject::tr("libcmex function call: ") << cmex_init(4) << endl;
// Функция из внешней библиотеки
qStdOut() << QObject::tr("libcmext function call: ") << cmext_init(9) << endl;
MyMainWindow* mmw = new MyMainWindow();
mmw->show();
return app.exec();
}
----
В файле `cmex/src/cmex/CMakeLists.txt` добавить новые файлы к списку
файлов, используемых для компиляции:
[source,cmake]
----
set(TRGT_sources
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/my_main_window.cpp
)
set(TRGT_uis
${CMAKE_CURRENT_SOURCE_DIR}/my_main_window.ui
)
----
[source,cmake]
----
# Цель для создания исполняемого файла
add_executable(${TRGT} ${TRGT_sources} ${TRGT_uis})
----
и добавить строки для подключения графических библиотек Qt5 и
соответствующих им заголовочных файлов:
[source,cmake]
----
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Gui_INCLUDE_DIRS})
target_include_directories(${TRGT} SYSTEM PUBLIC ${Qt5Widgets_INCLUDE_DIRS})
target_link_libraries(${TRGT} Qt5::Gui)
target_link_libraries(${TRGT} Qt5::Widgets)
----
Во время сборки проекта в файл переводов `cmex/l10n/cmex_app_ru_RU.ts`
будут добавлены новые строки, их нужно перевести с помощью `linguist` и
снова скомпилировать проект.