= CMake: управление проектом :title-separator: {sp}| :category: Программирование :tags: программирование, cmake, :toc: == Полезные ссылки * https://github.com/onqtam/awesome-cmake[Каталог ссылок] * https://cgold.readthedocs.io/en/latest/index.html[CGold: The Hitchhiker’s 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` создаётся, чтобы избежать попадания создаваемых во время сборки файлов в иерархию основного проекта. Запись результатов сборки проекта внутрь иерархии каталогов с исходными текстами приводит к засорению формируемыми на этапе сборки файлами, которые затрудняют разработку, поиск в оригинальных файлах и мешают ориентироваться в проекте. При работе с несколькими типами сборки, например, отладка и выпуск, появляется необходимость корректного полного удаления результатов предыдущего тип сборки. == Начало проекта Проект, в котором выполнены приведённые в данном разделе действия, можно посмотреть 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 git@git.246060.ru:f1x1t/cmlib.git cmake/cmlib git submodule add git@git.246060.ru:f1x1t/cmake-find.git cmake/find git submodule add git@git.246060.ru:f1x1t/cmake-generators.git cmake/generators git submodule add git@git.246060.ru:f1x1t/cmake-doc.git cmake/doc git submodule add git@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) ---- В каталоге `cmake/etc` требуется создать файл `Variables.cmake`, в котором требуется определить значения переменных, используемых библиотекой CMLib для автоматического создания пакетов, генерации документации: [#variables.cmake] [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 ") 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 .git/ obj.*/ _build _output files/lib files/log debian/tmp/ debian/files debian/.*\\\\.log$ debian/.*.substvars debian/stamp.* CMakeLists.txt.user.* cmake/lib/.git$ ~$ \\\\..*\\\\.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` <>. По умолчанию цель для упаковки исходных текстов называется `package_source`. В библиотеке CMLib определены значения основных параметров, а также дополнительная цель `dist`. Для создания бинарных пакетов нужно выполнить в каталоге сборки команду `cpack`. [source,sh] ---- cd _build/debug make dist cpack ---- == Примеры библиотек и приложений Напишу позже. == Базовая библиотека В файле `cmex/CMakeLists.txt` должна быть строка, включающая поиск файла `CMakeLists.txt` в подкаталоге `src/lib`: [source,cmake] ---- add_subdirectory(src/libcmex) ---- В каталоге `cmex/src/libcmex` нужно создать файл `cmex.hpp`: [source,cpp] ---- #ifndef LIBCMEX_CMEX_HPP_ #define LIBCMEX_CMEX_HPP_ #include int32_t cmex_init(int32_t i); #endif // LIBCMEX_CMEX_HPP_ ---- файл `cmex.cpp`: [source,cpp] ---- #include "cmex.hpp" int32_t cmex_init(int32_t i = 0) { return i; } ---- и файл `CMakeLists.txt`: [source,cmake] ---- # Название основной цели и имя библиотеки в текущем каталоге set(current_target cmex) # Список файлов исходных текстов set(current_target_sources ${CMAKE_CURRENT_SOURCE_DIR}/cmex.cpp ) # Список заголовочных файлов (используется для установки) set(current_target_headers ${CMAKE_CURRENT_SOURCE_DIR}/cmex.hpp ) add_common_library(TARGET ${current_target} SOURCES ${current_target_sources}) common_target_properties(${current_target}) # Цель, используемая только для установки заголовочных файлов, без компиляции проекта add_custom_target(${current_target}-install-headers COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=headers -P "${CMAKE_BINARY_DIR}/cmake_install.cmake" ) # Правила для установки install(TARGETS ${current_target}_static ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) if(BUILD_SHARED_LIBS) install(TARGETS ${current_target}_shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() install(FILES ${CMAKE_BINARY_DIR}/include/cmlib_config.hpp ${current_target_headers} COMPONENT headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${current_target}) ---- == Базовое приложение В файле `cmex/CMakeLists.txt` должна быть строка, включающая поиск файла `CMakeLists.txt` в подкаталоге `src/cmex`: [source,cmake] ---- add_subdirectory(src/cmex) ---- В каталоге `cmex/src/cmex` нужно создать файл `main.cpp`: [source,cpp] ---- #include "compiler_features.hpp" #include "cmlib_config.hpp" #include #include "cmex.hpp" int main(int argc, char **argv) { std::cout << CMEX_COMPILER_VERSION_MAJOR << std::endl; // Значение из compiler_features.hpp std::cout << BUILD_TYPE << std::endl; // Значение из cmlib_config.hpp std::cout << CMEX_VERSION_STR << std::endl; // Значение из cmlib_config.hpp std::cout << cmex_init(4) << std::endl; // Функция из внутренней библиотеки return 0; } ---- и файл `CMakeLists.txt`: [source,cmake] ---- # Название основной цели в текущем каталоге set(current_target cmex_app) # Список файлов исходных текстов set(current_target_sources ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ) # Цель для создания исполняемого файла add_executable(${current_target} ${current_target_sources}) common_target_properties(${current_target}) # Зависимость от библиотеки из текущего проекта add_dependencies(${current_target} cmex) # Добавление внутреннего каталога src/libcmex к списку путей для поиска заголовочных файлов target_include_directories(${current_target} PUBLIC $) # Имя выходного файла для цели (параметр OUTPUT_NAME) set_target_properties(${current_target} PROPERTIES OUTPUT_NAME cmex RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR} ) # Путь поиска библиотек внутри проекта link_directories(${CMAKE_INSTALL_LIBDIR}) # Сначала внутренние статические библиотеки target_link_libraries(${current_target} cmex_static) # Правила для установки install(TARGETS ${current_target} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) ---- == Подключение внешнего проекта В каталоге `cmex/thirdparty` нужно создать каталог `cmext` с проектом, состоящим из файла `cmext.hpp`: [source,c] ---- #ifndef CMEXT_CMEXT_HPP_ #define CMEXT_CMEXT_HPP_ #include int32_t cmext_init(int32_t i); #endif ---- файла `cmext.cpp`: [source,c] ---- #include "cmext.hpp" int32_t cmext_init(int32_t i = 0) { return i; } ---- и файла `CMakeLists.txt`: [source,cmake] ---- cmake_minimum_required(VERSION 3.3) project(cmext) include(GNUInstallDirs) add_library(cmext cmext.cpp) install(TARGETS cmext ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES cmext.hpp COMPONENT headers DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_PROJECT_NAME}) ---- В файле `cmex/CMakeLists.txt` нужно подключить стандартный модуль `ExternalProject` и описать правила для его загрузки, настройки, компиляции и установки для сопряжения с текущим проектом: [source,cmake] ---- # Подключение внешних проектов include(ExternalProject) ExternalProject_Add(cmext EXCLUDE_FROM_ALL TRUE SOURCE_DIR ${CMAKE_SOURCE_DIR}/thirdparty/cmext INSTALL_DIR ${CMAKE_BINARY_DIR} DOWNLOAD_COMMAND "" BUILD_BYPRODUCTS /lib/libcmext.a CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_BUILD_TYPE=Release ) ---- Вызовы этих функций нужно сделать до функций `add_subdirectories`, чтобы в подключенных подкаталогах можно было использовать цель `cmext` для определения зависимостей. В файле `cmex/src/cmex/CMakeLists.txt` нужно подключить внешний проект `cmext`: [source,cmake] ---- # Зависимость от библиотеки из внешнего проекта проекта add_dependencies(${current_target} cmext) ---- [source,cmake] ---- # Добавление каталога, в который устанавливаются заголовочные файлы от внешнего # проекта cmext, к списку путей для поиска заголовочных файлов target_include_directories(${current_target} PUBLIC $) ---- [source,cmake] ---- # Библиотека из внешнего проекта cmext target_link_libraries(${current_target} ${CMAKE_BINARY_DIR}/lib/libcmext.a) ---- Для проверки работоспособности в файле `cmex/src/cmex/main.cpp` нужно вызвать функцию `cmext_init` из библиотеки, предоставляемой внешним проектом. Например, можно заменить его содержимое на: [source,cpp] ---- #include "compiler_features.hpp" #include "cmlib_config.hpp" #include #include #include "cmex.hpp" QTextStream& qStdOut() { static QTextStream ts(stdout); return ts; } int main(int argc, char **argv) { // Значение из 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; } ---- == Qt5 Для поиска необходимых компонентов Qt5 нужно в файл `cmex/CMakeLists.txt` перед вызовом функции `cmlib_config_hpp_generate()` нужно добавить строку: [source,cmake] ---- find_package(Qt5 COMPONENTS Core Network Gui Widgets DBus Concurrent Sql REQUIRED) ---- Библиотека CMLib автоматически подключает вызов препроцессора `moc` и компилятора ресурсов `rcc`, если цель использует модуль `Core`, и вызывает компилятор файлов описания интерфейса, если цель использует модуль `Widgets`. == Консольное приложение В файл `cmex/src/cmex/CMakeLists.txt` добавить строки: [source,cmake] ---- # Qt5 qt_translation(TARGET ${current_target} TS_DIR ${CMAKE_SOURCE_DIR}/l10n LANGUAGES ru_RU) target_include_directories(${current_target} SYSTEM PUBLIC ${Qt5Core_INCLUDE_DIRS}) target_compile_options(${current_target} PUBLIC "${Qt5Core_EXECUTABLE_COMPILE_FLAGS}") target_link_libraries(${current_target} Qt5::Core) ---- Для проверки работоспособности подключения Qt5 файл `cmex/src/cmex/main.cpp` нужно заменить на: [source,cpp] ---- #include "compiler_features.hpp" #include "cmlib_config.hpp" #include #include #include #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] ---- MyMainWindow 0 0 678 415 Main Window ---- заголовочный файл `cmex/src/cmex/my_main_window.hpp`: [source,cpp] ---- #ifndef CMEX_MY_MAIN_WINDOW_HPP_ #define CMEX_MY_MAIN_WINDOW_HPP_ #include #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 #include #include #include #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(current_target_sources ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/my_main_window.cpp ) set(current_target_uis ${CMAKE_CURRENT_SOURCE_DIR}/my_main_window.ui ) ---- [source,cmake] ---- # Цель для создания исполняемого файла add_executable(${current_target} ${current_target_sources} ${current_target_uis}) ---- и добавить строки для подключения графических библиотек Qt5 и соответствующих им заголовочных файлов: [source,cmake] ---- target_include_directories(${current_target} SYSTEM PUBLIC ${Qt5Gui_INCLUDE_DIRS}) target_include_directories(${current_target} SYSTEM PUBLIC ${Qt5Widgets_INCLUDE_DIRS}) target_link_libraries(${current_target} Qt5::Gui) target_link_libraries(${current_target} Qt5::Widgets) ---- Во время сборки проекта в файл переводов `cmex/l10n/cmex_app_ru_RU.ts` будут добавлены новые строки, их нужно перевести с помощью `linguist` и снова скомпилировать проект.