Добавление нового языкового процессора

From EjudgeWiki

Навигация: Главная страница/Система ejudge/Расширение/Добавление нового языкового процессора.

Руководство по добавлению поддержки языков программирования, приведенное здесь, относится к системе ejudge версии не ниже 2.3.8.

Языковой процессор (ЯП) — это либо компилятор, либо интерпретатор некоторого языка программирования.

Поскольку система ejudge поддерживает несколько вариантов одного и того же языка программирования (например, Free Pascal, Borland Kylix, и т. д.), правильнее говорить не о поддержке языков программирования, а о поддержке языковых процессоров.

Добавление поддержки нового ЯП в систему ejudge выполняется в несколько шагов.

  1. Определение параметров добавляемого ЯП
  2. Написание настроечного скрипта (lang-version.in)
  3. Написание скрипта компиляции (lang.in)
  4. Переконфигурация поддерживаемых ЯП с помощью программы ejudge-configure-compilers
  5. Перезапуск системы ejudge

Определение параметров ЯП

Параметры ЯП, необходимые для его поддержки в системе ejudge, перечислены ниже:

arch архитектура ЯП
insecure флаг поддержки безопасного режима
short_name краткое название ЯП(как правило, определяется автоматически)
long_name полное название ЯП
src_sfx стандартный суффикс файла с исходным текстом для данного ЯП
exe_sfx стандартный суффикс исполняемого файла для данного ЯП
version версия ЯП
arg аргумент, указанный при конфигурировании ЯП программами ejudge-setup или ejudge-configure-compilers

Основной параметр ЯП — это архитектура (arch). Под архитектурой понимается системное окружение, в котором запускается тестируемая программа. Система ejudge поддерживает следующие архитектуры:

  • linux — статически скомпонованная исполняемая программа
  • linux-shared — динамически скомпонованная исполняемая программа (в том числе и скрипты)
  • java — байт-код java
  • msil — байт-код .NET
  • dos — приложения DOS

Архитектура linux является архитектурой по умолчанию. Если ЯП имеет архитектуру linux, то параметр arch для этого ЯП должен либо отсутствовать, либо иметь пустое значение.

Второй параметр ЯП — это флаг, корректно ли работает тестируемая программа для данного ЯП при запуске в безопасном режиме. Если ЯП имеет архитектуру linux, java или dos, то тестируемые программы для такого ЯП, как правило, корректно работают в безопасном режиме, при этом поддерживаются все ограничения безопасности. Если ЯП имеет архитектуру msil, то безопасный режим для него не поддерживаются. Программы для такого ЯП могут тестироваться, но при этом никаких ограничений безопасности на работу программы накладываться не будет. Если ЯП имеет архитектуру linux-shared, то безопасный режим для такого ЯП поддерживается, однако тестируемая программа в безопасном режиме может работать некорректно или не работать вообще.

В любом случае, если есть сомнения в работоспособности программы в безопасном режиме, требуется проверка на простых программах, как работающих со стандартными потоками, так и работающих с файлами.

Если добавляемый ЯП не поддерживает безопасный режим, то параметр ЯП insecure должен быть равен 1. Если безопасный режим поддерживается, то параметр должен отсутствовать или иметь пустое значение.

Из ЯП поддерживаемых системой ejudge в стандартной поставке в версии 2.3.8, небезопасными являются ЯП gcj (GNU Java), gfortran (GNU Fortran), mcs (Mono C#), mzscheme (MzScheme), php (PHP), vbnc (Mono Visual Basic), yabasic (YaBasic).

Параметр short_name — это краткое название ЯП. Краткое название используется в таблице посылок пользователей и ряде других мест. Краткое название ЯП — это, по сути, идентификатор ЯП. Он должен представлять собой одно слово, не длинее 32 знаков, состоящее из заглавных и строчных латинских букв, цифр и знаков +, -, _. Как правило, краткое название ЯП определяется автоматически из имени скрипта компиляции и настройки. Например, если скрипт компиляции (точнее, шаблон скрипта компиляции) имеет имя foo.in, то по умолчанию кратное название ЯП будет равно foo. Обратите внимание, что в unix-системах имена файлов чувствительны к регистру букв. Значение параметра short_name также чувствительно к регистру букв.

Параметр long_name — это развернутое (полное) название ЯП. Например, краткое имя ЯП может быть foo, а полное — "GNU Foo Interpreter". Обратите внимание, что в полное название ЯП не входит номер версии.

Параметр src_sfx — это стандартный суффикс исходных файлов для данного ЯП. Например, если для ЯП GNU Foo исходные файлы называются file.foo, то суффиксом исходных файлов будет строка .foo (обратите внимание, что "точка" входит в суффикс).

Параметр exe_sfx определяет стандартный суффикс для исполняемых файлов для данного ЯП. Как правило, суффикс исполняемых файлов зависит от архитектуры ЯП. Так, для архитектур linux и linux-shared стандартный суффикс исполняемых файлов не требуется, поэтому параметр exe_sfx может быть опущен или иметь пустое значение. Для архитектур dos и msil стандартный суффикс исполняемых файлов — .exe, а для архитектуры java — .jar.

Параметры arch, insecure, short_name, long_name, src_sfx, exe_sfx описывают свойства собственно ЯП, поэтому для данного ЯП не меняются от инсталляции к инсталляции системы ejudge.

Параметры version и arg описывают свойства инсталляции данного ЯП в конкретной системе, поэтому их значения могут меняться от инсталляции к инсталляции.

Параметр version определяет версию ЯП. Если значение этого параметра пусто, то считается, что соответствующий ЯП не был обнаружен в системе. Поэтому значение этого параметра не должно быть пустым. Версия ЯП определяется настроечным скриптом, как правило, с помощью запуска ЯП со специальными опциями и обработки полученного вывода.

Параметр arg содержит аргумент конфигурации ЯП, переданный в скрипты настройки ejudge-setup или ejudge-configure-compilers. Например, если запустить программу ejudge-configure-compilers с опцией --with-foo=/usr/local/bin/foo, то параметр /usr/local/bin/foo будет передан скрипту конфигурации ЯП foo-version. Кроме того, этот параметр может быть установлен и в интерактивном режиме в программах ejudge-setup и ejudge-configure-compilers. Как правило, этот параметр задает путь к ЯП в файловой системе, и полезен в случаях, когда ЯП размещается в нестандартных местах.

Структура файлов для поддержки ЯП

Предположим, что система ejudge проинсталлирована в каталог /opt/ejudge, а каталог для данных установлен в /home/ejudge. Тогда в каталоге /home/ejudge/compile размещаются конфигурационные файлы и скрипты для сервера компиляции программ.

compile.cfg

Файл /home/ejudge/compile/conf/compile.cfg — основной конфигурационный файл сервера компиляции compile. В файле содержится информация, необходимая для компиляции для всех поддерживаемых в данной инсталляции ЯП. Например, для ЯП foo секция описания ЯП может выглядить следующим образом:

[language]
id = 99
short_name = "foo"
long_name = "GNU Foo Interpreter"
src_sfx = ".foo"
cmd = "foo"

Здесь id — это числовой идентификатор ЯП, уникальный для всех ЯП в данной инсталляции. Остальная информация о ЯП берется из параметров ЯП, описанных в предыдущем разделе.

Файл /home/ejudge/compile/conf/compile.cfg автоматически записывается при каждом вызове программы ejudge-configure-compilers. Все изменения, внесенные в него вручную, теряются при каждом запуске ejudge-configure-compilers.

Версия ЯП и флаг, что соответствующий ЯП в системе обнаружен, не содержатся в этом конфигурационном файле, а считываются непосредственно из конфигурационного файла ЯП при каждом запуске программы compile.

lang_ids.cfg

Идентификаторы id ЯП, поддерживаемых в стандартной поставке, хранятся в файле ${prefix}/libexec/ejudge/lang/lang_ids.cfg. Если информация о новом ЯП в этом файле отсутствует, его идентификатор назначается автоматически: берется первый еще не использованный в файле compile.cfg. Пользователь может скопировать файл lang_ids.cfg в каталог ${prefix}/compile/scripts и вносить в него произвольные изменения. Модифицировать файл ${prefix}/libexec/ejudge/lang/lang_ids.cfg не рекомендуется, так как он будет перезаписан при обновлении системы.

Каталог lang.d

Каталог /home/ejudge/compile/conf/lang.d содержит файлы конфигурационных настроек всех поддерживаемых системой ejudge ЯП. Для ЯП foo конфигурационный файл называется foo.cfg. Его содержимое может быть примерно таким:

version="3.14"
arg="/usr/local/bin/foo"
long_name="GNU Foo Interpreter"
src_sfx=".foo"
arch="linux-shared"
FOOPATH="/usr/local/bin/foo"

Файл записывается в синтаксисе /bin/sh. Этот файл используется как программами системы ejudge, такими как compile, super-serve, так и непосредственно скриптами foo и foo-version. Он генерируется скриптом foo-version, запускаемым в специальном режиме.

Помимо параметров ЯП, описанных выше, файл может содержать определения произвольных переменных, необходимых для работы скриптов foo или foo-version. На примере выше это переменная FOOPATH.

Каталог in

В каталоге in находятся заготовки для скриптов конфигурации и компиляции ЯП. В каталоге /opt/ejudge/libexec/ejudge/lang/in находятся заготовки скриптов для ЯП, поддерживаемых в стандартной поставке системы ejudge. В каталоге /home/ejudge/compile/scripts/in находятся заготовки скриптов пользователя. Последний каталог более приоритетен, то есть если заготовка скрипта обнаружена в каталоге /home/ejudge/compile/scripts/in, системный каталог проверяться не будет. Поэтому, если необходимо модифицировать поставляемый скрипт, его нужно предварительно скопировать в этот каталог, затем модифицировать. Модифицировать файлы непосредственно в каталоге /opt/ejudge/libexec/ejudge/lang/in не рекомендуется, так как эти файлы перезаписываются при обновлении системы ejudge.

Каталог scripts

В каталоге /home/ejudge/compile/scripts находятся обработанные заготовки скриптов, готовые к использованию всеми программами системы ejudge. Заготовки скриптов обрабатываются и копируются из каталогов /opt/ejudge/libexec/ejudge/lang/in и /home/ejudge/compile/scripts/in в этот каталог программой ejudge-configure-compilers.

Заготовки скриптов

Для ЯП foo должны быть написаны заготовки скриптов foo.in и foo-version.in. Заготовки скриптов должны быть помещены в каталог /home/ejudge/compile/scripts/in. Заготовки скриптов по смыслу соответствуют заготовкам файлов Makefile.in, config.h.in в системе GNU autotools. То есть в заготовках скриптов выполняется замена переменных вида @var@ на их значения.

Поддерживаемые подстановки перечислены в таблице

подстановка соотв. опция configure значение для рассм. примера
@lang_config_dir@ --enable-lang-config-dir /home/ejudge/compile/conf/lang.d
@prefix@ --prefix /opt/ejudge
@exec_prefix@ --exec-prefix /opt/ejudge
@libexecdir@ --libexecdir /opt/ejudge/libexec
@local_dir@ --enable-local-dir /var/lib/ejudge
@contests_home_dir@ --enable-contests-home-dir /home/ejudge

Настроечный скрипт

Интерфейс

Настроечный скрипт должен поддерживать несколько режимов запуска. Режимы задаются с помощью опций командной строки. Все режимы работы рассматриваются на примере настроечного скрипта foo-version

-r — режим конфигурирования

foo-version [-v] -r [ARG]

В режиме конфигурирования должна быть проверена поддержка ЯП в данной инсталляции. На стандартный поток вывода должно быть напечатано новое содержимое конфигурационного файла ЯП foo.cfg. Кроме того, если указана опция -v, на стандартный поток ошибок должна быть напечатана дополнительная информация о процессе конфигурирования. При успешном конфигурировании код завершения должен быть равен 0, а при неудаче — 1. В параметре ARG передается путь, который указал пользователь в опции --with-foo=ARG при конфигурировании в пакетном режиме, либо путь, введенный им при конфигурировании в диалоговом режиме.

Так, если ЯП foo поддерживается в данной инсталляции, сценарий запуска может быть следующим:

bash$ foo-version -v -r /usr/local/bin/foo
version="3.14"
arg="/usr/local/bin/foo"
long_name="GNU Foo Interpreter"
src_sfx=".foo"
arch="linux-shared"
FOOPATH="/usr/local/bin/foo"
checking whether GNU Foo is available... yes, /usr/local/bin/foo, 3.14
bash$

Последняя строка (checking...) выводится на стандартный поток ошибок.

Если ЯП foo не поддерживается в данной инсталляции, сценарий запуска может быть следующим:

bash$ foo-version -v -r
version=
arg=
long_name="GNU Foo Interpreter"
src_sfx=".foo"
arch="linux-shared"
FOOPATH="/bin/false"
checking whether GNU Foo is available... no
bash$

Последняя строка (checking...) выводится на стандартный поток ошибок.

-l — режим получения списка

foo-version -l

В режиме получения списков ЯП на стандартный поток вывода должна быть напечатана строка информации о ЯП. Строка информации печатается независимо от того, поддерживается ли данный ЯП в данной инсталляции. Код завершения всегда должен быть равен 0.

Так, для ЯП foo сценарий запуска может быть следующим:

bash$ foo-version -l
GNU Foo Interpreter [1]
bash$

Режим получения списка ЯП используется программой ejudge-configure-compilers при запуске ее с опцией --list.

-p — печать пути к ЯП

foo-version -p

В режиме печати пути к ЯП на стандартный поток вывода должен быть напечатан путь к ЯП. Код завершения должен быть равен 0. Если ЯП не поддерживается в данной инсталляции, вывод должен быть пуст, а код завершения равен 1. В этом случае на стандартный поток ошибок может выводиться диагностическое сообщение.

Так, если ЯП foo поддерживается в данной инсталляции, сценарий запуска может быть следующим:

bash$ foo-version -p
/usr/local/bin/foo
bash$

Режим печати используется программой super-serve в некоторых специальных случаях.

-f — печать полного имени и версии

foo-version -f

В режиме печати полного названия ЯП на стандартный поток вывода должно быть напечатано полное имя ЯП и номер версии ЯП. В случае, если ЯП не поддерживается на данной системе (в данной инсталляции), стандартный поток вывода должен быть пуст, а код возврата равен 1. На стандартный поток ошибок при этом может выводиться диагностическое сообщение.

Так, если ЯП foo поддерживается в данной инсталляции, сценарий запуска может быть следующим:

bash$ foo-version -f
GNU Foo Interpreter 3.14
bash$

Если ЯП foo не поддерживается, то следующим:

bash$ foo-version -f
This language is not supported.
bash$

в этом случае сообщение выводится на стандартный поток ошибок.

Режим печати версии используется программой super-serve в процессе настройки ЯП при редактировании турнира.

печать версии

foo-version

В режиме печати версий, который задаётся запуском скрипта без параметров, на стандартный поток вывода должен быть напечатан номер версии ЯП. В случае, если ЯП не поддерживается на данной системе (в данной инсталляции), стандартный поток вывода должен быть пуст, а код возврата равен 1. На стандартный поток ошибок при этом может выводиться диагностическое сообщение.

Так, если ЯП foo поддерживается в данной инсталляции, сценарий запуска может быть следующим:

bash$ foo-version
3.14
bash$

Если ЯП foo не поддерживается, то следующим:

bash$ foo-version
This language is not supported.
bash$

в этом случае сообщение выводится на стандартный поток ошибок.

Режим печати версии используется программой super-serve в процессе настройки ЯП при редактировании турнира.

Реализация

Рассмотрим реализацию скрипта конфигурирования ЯП foo foo-version.in. За образец для этого (гипотетического) скрипта взят скрипт для конфигурирования ЯП Ruby.

Скрипт компиляции

Скрипт компиляции принимает в качестве параметров имя файла с исходным текстом программы для ЯП и имя файла результата. Задача скрипта компиляции — получить на выходе файл, готовый к запуску в окружении, задаваемом архитектурой языка.

Для компилируемых языков результатом работы скрипта компиляции является бинарный исполняемый файл. Для интерпретируемых языков результатом работы скрипта компиляции является скрипт, готовый к запуску. Как правило, для интерпретируемых языков для этого достаточно записать в начало файла конструкцию

#! INTERP-PATH ARGS

и установить бит x разрешения выполнения файла. Здесь INTERP-PATH — это полный путь к интерпретатору соответствующего языка.

Далее скрипт компиляции рассматривается на примере гипотетического интерпретируемого языка foo.

Интерфейс

Интерфейс скрипта компиляции очень простой

foo SRC-FILE DST-FILE

Здесь SRC-FILE — имя входного файла, DST-FILE — имя выходного файла. Система ejudge формирует имена входного и выходного файлов с использованием суффиксов исходного файла и файла результата, как указано в конфигурационном файле ЯП. Таким образом, никакой дополнительной обработки имен входного и выходного файла выполнять не требуется.

Дополнительно в переменной окружения EJUDGE_FLAGS могут задаваться флаги для ЯП. Администратор турнира имеет возможность устанавливать флаги при редактировании турнира. Поэтому скрипт компиляции должен поддерживать задание дополнительных опций с помощью еременной окружения EJUDGE_FLAGS.

Реализация

Пример реализации скрипта компиляции foo.in. За образец для этого (гипотетического) скрипта взят скрипт для компиляции ЯП Ruby.

Переконфигурация поддерживаемых ЯП

После того, как скрипты foo-version.in и foo.in помещены в каталог /home/ejudge/compile/scripts/in достаточно запустить программу ejudge-configure-compilers. Если скрипты написаны правильно, версия ЯП должна быть определена корректно, после чего язык готов к использованию.

В турнир язык может быть добавлен при редактировании настроек турнира с помощью CGI-программы serve-control.

Перезапуск ejudge

После выполнения всех шагов не забудьте перезапустить ejudge, чтобы сервер компиляции compile считал новый конфигурационный файл.