ИдеяСоздать кроссплатформенную консольную версию Блекбокса
МотивацияЭто позволит некоторым категориям граждан (и неграждан
) пользоваться Блекбоксом более привычным способом (похожим на использование, например,
fpc и
gcc), легко интегрировать компилятор со своим любимым редактором, а также легко разворачивать проект на системах, не имеющих графической оболочки (например, на серверах).
Собственно, работа уже идёт, о чём выпущено несколько видеоотчётов на канале
«Программирование на Обероне» (подписывайтесь, ставьте лайк
):
https://youtu.be/96fzbWtFHcQТекущее положение дел в Блекбоксе(здесь речь идёт только о возможностях компиляции из консоли!)Сейчас в ББ (
BBCP) есть возможность компилировать модули и компоновать их в исполняемые файлы из консоли, но задача эта порой нетривиальная даже для тех, кто «в теме». Приходится писать что-то вроде:
Код:
echo "ConsCompiler.Compile('', 'MyProgram.Mod')" | env BB_PRIMARY_DIR="BlackBox" BB_SECONDARY_DIR="$PWD" ./blackbox
На Виндоусе команды, а главное ключи, выглядят по-другому.
Кроме того, каждый модуль необходимо компилировать отдельно в правильном порядке. Корректно программно зафиксировать ошибки не получится, потому что blackbox всегда заканчивается с кодом возврата 1. После компиляции необходимо запустить команду компоновки, которая выглядит так:
Код:
env BB_PRIMARY_DIR="BlackBox" BB_SECONDARY_DIR="$PWD" ./blackbox <<DATA
Dev2Linker1.LinkElf Linux MyProgram:= Kernel$+ Files HostFiles Math Strings Dates Meta Log Dialog Services Fonts Ports Stores Converters Sequencers Models Printers Views Controllers Properties Printing Mechanisms Containers Documents Windows Console StdInterpreter HostConsole HostRegistry HostFonts HostWindows HostDates HostDialog StdDialog HostLang TextModels TextRulers TextSetters TextViews TextControllers TextMappers StdApi StdCmds StdLinks HostTextConv Args HostArgs StdLog ConsLog MyProgram
DATA
Набор модулей необходимо подбирать самому, так как их организация относится к внутреннему устройству ББ и, к сожалению, нигде не задокументирована. Мы этот барьер с горем пополам преодолели, но большинство пользователей не захотят связываться с такими проблемами.
К тому же, получившаяся программа будет фактически в тайне от программиста воспринимать переменные окружения типа
BB_SECONDARY_DIR. Отключить это при сборке проекта не представляется возможным. Для этого необходимо в нескольких местах редактировать исходный код модуля
HostFiles, о чём рядовой пользователь не догадается из-за недостатка документации по этому вопросу.
Разумеется, всё это никуда не годится.
Техническое заданиеВ процессе работы постепенно вырисовалось техническое задание к данной версии компилятора. (Исполнимый файл здесь назван «foc»)
Расположение файлов с исходным кодомЧтобы написать небольшую программу на Обероне (на Компонентном Паскале), программист обыкновенно создаёт пустой каталог. Например, «/home/user/prg/gauss» или «C:\Prg\Gauss». Назовём этот каталог
рабочим каталогом. В нём программист создаёт текстовые файлы с исходным кодом модулей. Например:
Код:
C:\Prg\Gauss\Gauss.Mod
C:\Prg\Gauss\GaussSolve.Mod
C:\Prg\Gauss\GaussShow.Mod
Имена файлов и имена модулей совпадают с точностью до расширения.
[Идея: позволить суффиксы, связанные со средой. Например, «HostGraph_Win32.Mod».]Как вариант, если имя модуля состоит из двух частей, его можно поместить в подкаталог (как в ББ):
Код:
C:\Prg\Gauss\Gauss.Mod
C:\Prg\Gauss\Gauss\Solve.Mod ←
C:\Prg\Gauss\Gauss\Show.Mod ←
Или совсем как в ББ:
Код:
C:\Prg\Gauss\Gauss.Mod
C:\Prg\Gauss\Gauss\Mod\Solve.Mod ←
C:\Prg\Gauss\Gauss\Mod\Show.Mod ←
Процесс компиляцияПрограммист, находясь в рабочем каталоге, даёт консольную команду:
Код:
foc Gauss
Варианты:
Код:
foc.exe Gauss
foc Gauss.Mod
foc -o Gauss.exe Gauss.Mod
foc --gui -o Gauss.exe Gauss.Mod
или, находясь в другом каталоге:
foc C:\Prg\Gauss\Gauss
или:
foc ..\..\Prg\Gauss\Gauss.Mod
или:
foc -o C:\Gauss.exe ..\..\Prg\Gauss\Gauss.Mod
в последних случаях компилятор воспринимает указанный каталог как рабочий
Построение графа импортаКомпилятор рекурсивно обходит все модули проекта, начиная с указанного «главного» модуля, и составляет граф импорта, компилирует каждый модуль (кроме системных, см. ниже) и создаёт исполнимый файл (имя и расположение которого можно задать ключом «-o»). Системные модули соответствующим образом помечаются. Месторасположение всех файлов фиксируется. При сборе информации о модулях, импортируемых из данного, анализируется либо исходный код (ищется секция
IMPORT; это уже реализовано, см.
видеоотчёт №1), либо символьный файл (при отсутствии исходного кода данного модуля).
Системный каталогКомпилятор также знает о месторасположении
системного каталога — того, в котором находятся так называемые
системные модули, такие как:
In, Out (Log), Math, Random, Strings, Files, Graph. Модули в системном каталоге скомпилированы заранее, компилятор ищет только
Code- и
Sym-файлы соответствующих модулей.
Месторасположение системного каталога (дальше варианты):
- или задаётся ключом «--sysdir»;
- или задаётся специальной переменной окружения;
- или совпадает с расположением исполнимого файла «foc.exe» (в Линуксе — «foc»);
- или известно компилятору априори ("захардкожено"), в том числе, захардкожеными могут быть несколько каталогов, последовательно проверяемых компилятором.
Файлы с машинным кодом и символьные файлы Файлы откомпилированных модулей записываются в виде
ocf- и
osf-файлов в каталогах
Code и
Sym, которые создаются рядом с соответствующим
Mod-файлом (или каталогом
Mod, если используется вариант расположения файлов, принятый в ББ).
Ниже показан нетипичный, но возможный, пример:
Код:
C:\Prg\Gauss\Gauss.Mod
C:\Prg\Gauss\GaussSolve.Mod
C:\Prg\Gauss\GaussShow.Mod
C:\Prg\Gauss\Code\Gauss.ocf
C:\Prg\Gauss\Code\GaussSolve.ocf
C:\Prg\Gauss\Code\GaussShow.ocf
C:\Prg\Gauss\Sym\Gauss.osf
C:\Prg\Gauss\Sym\GaussSolve.osf
C:\Prg\Gauss\Sym\GaussShow.osf
Порядок поиска файлов модулейТак как системные модули заранее скомпилированы, то их исходный код не нужен компилятору и даже может отсутствовать, поэтому компилятор в обычном режиме работы не должен искать Mod-файлов системных модулей.
Некоторые модули, находящиеся в рабочем каталоге, также могут быть скомпилированы заранее и не иметь исходного кода, если они изготовляются другим программистом или их исходный код специально держится в тайне (например, в ходе решения олимпиадной или экзаменационной задачи).
Если системный модуль импортирует некий модуль, но модуль с таким именем есть в рабочем каталоге, используется последний. Таким образом, программисту даётся простой способ заместить некоторые системные модули своими версиями только в рамках данного проекта.
Сами системные модули тоже можно замещать, используя команду:
Код:
foc --install Math
Когда у компилятора возникает задача найти какой-нибудь модуль, он знает, какой вариант из двух возможных верен:
а) модуль следует искать только в рабочем каталоге,
б) модуль следует искать сначала в рабочем каталоге, а затем в системном.
К первому варианту относится главный модуль. Например, команда:
Код:
foc Math
при отсутствующих в рабочем каталоге соответствующих файлах должна приводить к ошибке, а не к попытке компиляции системного модуля Math.
ПримерПусть в определённый момент компилятор должен найти файл модуля EngineMain, причём по варианту
б (см. выше).
Тогда порядок поиска будет следующий:
1. В рабочем каталоге: EngineMain.Mod
2. В рабочем каталоге: Sym\EngineMain.osf ←
3. В рабочем каталоге: Engine\Main.Mod
4. В рабочем каталоге: Engine\Mod\Main.Mod
5. В рабочем каталоге: Engine\Sym\Main.osf ←
6. В системном каталоге: Sym\EngineMain.osf
7. В системном каталоге: Engine\Sym\Main.osf
Поиск идёт по порядку и прерывается, как только файл модуля впервые найден.
В случае нахождения файла в пунктах 2 и 5 необходимо проверить также и наличие файла с машинным кодом. Соответственно:
2а. В рабочем каталоге: Code/EngineMain.ocf
5а. В рабочем каталоге: Engine/Code/Main.ocf
Аналогично при нахождении файла в пунктах 6 и 7:
6а. В системном каталоге: Code/EngineMain.ocf
7а. В системном каталоге: Engine/Code/Main.ocf
Системные модулиЕсли в результате поиска файла модуля он был найден в системном каталоге, то такой модуль называется
системным модулем.
Необходимо предусмотреть команду для перекомпиляции системного модуля или копирования модуля из рабочего каталога в системный каталог. В последним случае модуль становится системным. Это называется
установка.
Команда установки могла бы выглядеть так:
Код:
foc --install Graph
(компилирует модуль
Graph, находящийся обязательно в рабочем каталоге, и копирует его в системный каталог; то есть копирует все три файла:
Mod, osf, ocf)