= Программный проект и иерархия каталогов :title-separator: {sp}| :category: Программирование :tags: Linux, программирование, cmake Для операционных систем типа Linux принят стандарт https://ru.wikipedia.org/wiki/FHS[FHS] («стандарт иерархии файловой системы»), унифицирующий местонахождение файлов и каталогов с общим назначением в файловой системе. Полная текущая версия стандарта находится http://refspecs.linuxfoundation.org/fhs.shtml[здесь]. == Типы расположения проекта В соответствии с данным стандартом, а также принятыми в ведущих дистрибутивах правилами размещения исполняемых файлов в каталогах пользователей, можно выделить следующие типы расположения: * системная иерархия в каталоге `/usr` используется для установки бинарных пакетов для данного дистрибутива; * системная иерархия в каталоге `/usr/local` используется для установки программного обеспечения системным администратором без использования пакетов (не рекомендуется для использования из-за проблем поддержки в актуальном состоянии); * системная иерархия в каталоге `/opt` используется для установки стороннего программного обеспечения. В рамках данной иерархии предполагается, что каждый программный продукт располагается в собственном каталоге. При таком типе сборки обычно используются дополнительные методы (статическая компоновка, включение в состав пакета своего набора динамических библиотек) для обеспечения работы пакета в операционных системам с отличающимся составом библиотек и другим циклом обновления; * системная иерархия в домашнем каталоге пользователя не имеет определённого стандарта, обычно производители дистрибутивов предлагают использовать для исполняемых файлов каталоги `$HOME/bin` или `$HOME/.local/bin`. Система автоматизации сборки программного обеспечения `CMake` позволяет организовать окружение подобное перечисленным выше. На этапе сборки проекта можно создать структуру каталогов, которая будет отвечать требованиям по логическому разделению файлов на исполняемые, заголовочные, библиотеки, файлы настроек и т.д. == Автоматическая адаптация к текущему окружению Для обеспечения единообразной работы вне зависимости от варианта иерархии каталогов, в которой находится исполняемый файл, можно выполнять автоматическую настройку на работу в текущем окружении. В библиотеке https://git.246060.ru/f1x1t/myxlib[myxlib] реализован класс, который анализирует расположение и окружение исполняемого файла и предоставляет методы для получения имён каталогов, соответствующих текущему окружению. Названия методов и описания возвращаемых значений приведены в таблице. [cols="2m,4",options="header",] |=== | Метод | Описание | homeDirectory() | Полный путь к домашнему каталогу текущего пользователя | tempDirectory() | Полный путь к каталогу с временными файлами | userConfigDirectory() | Полный путь к пользовательскому каталогу с файлами настройки | userConstDataDirectory() | Полный путь к пользовательскому каталогу с неизменяемыми файлами | userVarDataDirectory() | Полный путь к пользовательскому каталогу с изменяемыми файлами | userLogDirectory() | Полный путь к пользовательскому каталогу с журналами работы | executableFilePath() | Полный путь к исполняемому файлу | systemConfigDirectory() | Полный путь к системному каталогу с файлами настройки | systemConstDataDirectory() | Полный путь к системному каталогу с неизменяемыми файлами | systemVarDataDirectory() | Полный путь к системному каталогу с изменяемыми файлами | systemLogDirectory() | Полный путь к системному каталогу с журналами работы | executableFileDirectory() | Полный путь к каталогу с исполняемым файлом | executableFileName() | Имя исполняемого файла | configFilePath() | Полный путь к файлу настройки | configFileName() | Имя файла настройки | projectName() | Имя подкаталога для проекта |=== Пример использования: [source,cpp] ---- #include namespace MF = myx::filesystem; MF::Paths& paths = MF::Paths::instance(); paths.init( QStringLiteral( "project_name" ), QStringLiteral( "conf" ) ); qDebug() << paths.systemConstDataDirectory().path(); ---- === Правила выбора типа окружения Класс `myx::filesystem::Paths` реализован в виде синглтона, чтобы повторно не выполнять проверку окружения в разных частях программы. Сначала определяются имена пользовательского и временного каталогов с помощью вызовов функций https://doc.qt.io/qt-5/qdir.html#homePath[`QDir::homePath`] и https://doc.qt.io/qt-5/qdir.html#tempPath[`QDir::tempPath`], затем имена пользовательских каталогов для настроек, постоянных и изменяемых данных и журналов. Эти значения не зависят от расположения исполняемого, а определяются согласно значениям переменных окружения, либо значениям принятыми в стандартах. Пример имён каталогов для пользователя `user` и проекта `project` приведён в таблице. [cols="3,3m,5m",options="header",] |=== | Назначение каталога | Метод | Значение | Домашний каталог | homeDirectory() | /home/user | Временные файлы | tempDirectory() | /tmp | Файлы настройки | userConfigDirectory() | /home/user/.config/project | Неизменяемые файлы | userConstDataDirectory() | /home/user/.local/share/project/data | Изменяемые файлы | userVarDataDirectory() | /home/user/.local/share/project/lib | Журналы работы | userLogDirectory() | /home/user/.local/share/project/log |=== ==== Общая проверка Для определения типа текущего окружения используется полный путь к исполняемому файлу, если он находится в каталоге `bin`, то выполняются проверки работы в одной из возможных вариантов иерархий, иначе делается заключение о том, что файлы всех типов находятся в одном каталоге с исполняемым и дальнейшие проверки не выполняются. ==== Проверка на работу в иерархии `/opt` Если полный путь к исполняемому файлу начинается c `/opt` и содержит в себе название текущего проекта, например `/opt/org/project/bin/application`, то выполняется проверка на наличие сопутствующих системных каталогов. Если они присутствуют, то принимается решение, что окружение в иерархии `/opt` сформировано правильно, иначе делается заключение о том, что файлы всех типов находятся в одном каталоге с исполняемым и дальнейшие проверки не выполняются. Пример правильной структуры каталогов для данной иерархии приведён в таблице. [cols="4,4m,6m",options="header",] |=== | Назначение файла / каталога | Метод | Значение | Исполняемый файл | executableFilePath() | /opt/org/project/bin/application | Файлы настройки | systemConfigDirectory() | /opt/org/project/etc | Неизменяемые файлы | systemConstDataDirectory() | /opt/org/project/files/data | Изменяемые файлы | systemVarDataDirectory() | /opt/org/project/files/lib | Журналы работы | systemLogDirectory() | /opt/org/project/files/log |=== ==== Проверка на работу в иерархии `/usr/local` Если полный путь к исполняемому файлу начинается c `/usr/local`, например `/usr/local/bin/application`, то выполняется проверка на наличие сопутствующих системных каталогов. Если они присутствуют, то принимается решение, что окружение в иерархии `/usr/local` сформировано правильно, иначе делается заключение о том, что файлы всех типов находятся в одном каталоге с исполняемым и дальнейшие проверки не выполняются. Пример правильной структуры каталогов для данной иерархии приведён в таблице. [cols="4,4m,5m",options="header",] |=== | Назначение файла / каталога | Метод | Значение | Исполняемый файл | executableFilePath() | /usr/local/bin/application | Файлы настройки | systemConfigDirectory() | /usr/local/etc/project | Неизменяемые файлы | systemConstDataDirectory() | /usr/local/share/project | Изменяемые файлы | systemVarDataDirectory() | /var/lib/project | Журналы работы | systemLogDirectory() | /var/log/project |=== ==== Проверка на работу в иерархии `/usr` Если полный путь к исполняемому файлу начинается c `/usr`, например `/usr/bin/application`, то выполняется проверка на наличие сопутствующих системных каталогов. Если они присутствуют, то принимается решение, что окружение в иерархии `/usr` сформировано правильно, иначе делается заключение о том, что файлы всех типов находятся в одном каталоге с исполняемым и дальнейшие проверки не выполняются. Пример правильной структуры каталогов для данной иерархии приведён в таблице. [cols="4,4m,5m",options="header",] |=== | Назначение файла / каталога | Метод | Значение | Исполняемый файл | executableFilePath() | /usr/bin/application | Файлы настройки | systemConfigDirectory() | /etc/project | Неизменяемые файлы | systemConstDataDirectory() | /usr/share/project | Изменяемые файлы | systemVarDataDirectory() | /var/lib/project | Журналы работы | systemLogDirectory() | /var/log/project |=== ==== Проверка на работу в домашнем каталоге Если полный путь к исполняемому файлу начинается c `/home/user/bin` или `/home/user/.local/bin`, например `/home/user/bin/application`, то выполняется проверка на наличие сопутствующих системных каталогов. Если они присутствуют, то принимается решение, что окружение в домашнем каталоге сформировано правильно, иначе делается заключение о том, что файлы всех типов находятся в одном каталоге с исполняемым и дальнейшие проверки не выполняются. Пример правильной структуры каталогов для данной иерархии приведён в таблице. [cols="4,4m,5m",options="header",] |=== | Назначение файла / каталога | Метод | Значение | Исполняемый файл | executableFilePath() | /home/user/bin/application | Файлы настройки | systemConfigDirectory() | /home/user/.config/project | Неизменяемые файлы | systemConstDataDirectory() | /home/user/.local/share/project/data | Изменяемые файлы | systemVarDataDirectory() | /home/user/.local/share/project/lib | Журналы работы | systemLogDirectory() | /home/user/.local/share/project/log |=== ==== Проверка на работу в окружении для разработки Если исполняемый файл находится в каталоге `bin` и при этом окружение не совпадает ни с одним из перечисленных выше, то делается предположение, что исполняемый файл запускается из окружения, сформированного или `/home/user/.local/bin`, например `/home/user/bin/application`, то выполняется проверка на наличие сопутствующих системных каталогов. Если они присутствуют, то принимается решение, что окружение в домашнем каталоге сформировано правильно, иначе делается заключение о том, что файлы всех типов находятся в одном каталоге с исполняемым и дальнейшие проверки не выполняются. Пример правильной структуры каталогов для данной иерархии приведён в таблице. [cols="3,4m,5m",options="header",] |=== | Назначение файла / каталога | Метод | Значение | Исполняемый файл | executableFilePath() | /home/user/bin/application | Файлы настройки | systemConfigDirectory() | /home/user/.config/project | Неизменяемые файлы | systemConstDataDirectory() | /home/user/.local/share/project/data | Изменяемые файлы | systemVarDataDirectory() | /home/user/.local/share/project/lib | Журналы работы | systemLogDirectory() | /home/user/.local/share/project/log |===