[devel] re-writing GNU C; part1.3.2: how to apply (WIP)
Ivan Zakharyaschev
imz на altlinux.org
Ср Янв 27 21:04:38 MSK 2016
On Wed, 27 Jan 2016, Ivan Zakharyaschev wrote:
> Шлю записки о том, как начать применять cuglify/Process в работе с
> какой-то "инородной" платформой FOO/Linux (без GCC). Скажем, в работе
> Один вариант -- более основательный и ценный для будущего, второй
> вариант, который я могу представить (и уже опробовал на практике) --
> проще и позволяет побыстрее увидеть результат компиляции.
> (Третий вариант, который я пытаюсь сейчас осуществить и который
> достижим побыстрее, чем первый основательный, опишу подробнее в
> следующей части сообщения.)
Итак, "второй вариант" я осуществил на практике. (С поправкой на то,
что Process не понимал расширения .i входного файла.)
Теперь хочется, чтобы всё происходило более гладко и незаметно для
собирающего под FOO/Linux, при этом давая ему уже реализованные
преимущества. И нам не до того, чтобы копаться со сборкой .hs via-С
("первый вариант").
Двигаемся к этой цели. (Это "третий вариант", который где-то между
полным "первым" и ручным "вторым" и удобнее с точки зрения
автоматизации; берём пример с ccache или скорее distcc):
Обращения к x86-машине за результатами вычислений, нужных при сборке
--------------------------------------------------------------------
Давайте вставим Process во что-то вроде distcc. Вот как это
будет работать:
1. на компилируемом файле исполняется препроцессор
2. результат пропускается через Process на удалённой x86-машине
3. результат принимается обратно на FOO/Linux и скармливается foo-cc
Пояснение такой схемы в общем:
раз на FOO/Linux какие-то инструменты-преобразователи (наш
Process) невозможно запустить, то мы во время сборки на FOO/Linux
попросим x86-машину сделать это вычисление за нас.
(Заметим, что сборочная система для Sisyphus ориентирована на нативную
сборку. И здесь мы действуем с этой стороны, т.е. пропихиваем сборку
со стороны FOO/Linux, а не так:
> Тут была мысль для удобства доделать Process на предмет вызова
> cpp/foo-cc по ssh сразу, вместо локального cpp/gcc, чтобы облегчить
> ход действий
)
Шаги 1.-3. этой схемы работы надо уточнить/усложнить в связи со
следующими соображениями.
Формулировка задачи Process как фильтра, работающего с семантикой
-----------------------------------------------------------------
Process является такого рода фильтром, целью которого является
поддержка задуманной программистом (и мейнтейнером пакета) семантики
его C-кода с использованием в качестве backend-а не GCC, а foo-cc.
Задуманная семантика может выражаться не только в C-коде (со своими
особыми конструкциями вроде nested functions, которые мы переписываем
из-за ограничений backend-а), но и в опциях gcc. Опции, работа которых
в foo-cc нас не устраивает, мы будем исполнять по возможности сами, а
опции для backend-а переписывать (хотя в простом случае просто
передаём их без изменений). Это знание о семантике некоторых опций GCC
и особенностях их реализации в foo-cc закладывается в Process.
Process управляет командами
---------------------------
Соответственно, в этой схеме работы на шаге:
1. опции препроцессора контролируются Process
2. --
3. опции foo-cc контролируются Process
Сейчас Process (как и примеры в language-c) устроен так, что он
умеет 1. (сам вызывает локальный cpp со всеми релевантными опциями), а 3.
не было у них в language-c задумано (ну потому что не было у них цели
вызывать cc для завершения анализа исходников).
К тому же такой контроль осложняется тем, что Process будет
запускаться удалённо, и от него мы должны получить команды для
запуска:
1. cpp
2. --
3. foo-cc
Т.е. помимо stdin для .c/.i, stdout для переработанного .i и stderr
для warnings, нужны каналы для дачи команд cpp и cc.
открытый вопрос: устройство вызова Process из distcc-подобного клиента
======================================================================
Посмотреть устройство distcc (или ccache) на предмет того, как можно
было бы его использовать для врезания cugligy/Process (запускаемого на
другой машине) в foo-cc/gcc.
Связанная с этой темой проблема, которая имеется в моей модельной
реализации взаимодействия distcc-подобного клиента и Process (см.
ниже) -- это то, что последняя полученная команда (собственно `foo-cc -c`)
выполняется в параллельном процессе, хотя как основная по смыслу и
последняя должна бы и быть тем, что работает до конца и отдаёт код
возврата.
Если немного подумать, наверное, можно переустроить скрипт; просто я
пока не додумал.
(Происходит эта проблема с параллельностью из того, что, во-первых, я
писал скрипт как можно проще, чтобы продемонстрировать идею, а
во-вторых, сама команда получается по каналу из Process, который мы
слушаем параллельно, так, чтобы не блокировать сам вызов Process, т.е.
слушатели запускаются до вызова Process и могут получить команду
только в то время пока Process работает, и сразу же приступают к
выполнению.)
Другими словами, одно дело fork-и-exec, а другое -- exec. Первое
должно делаться со вспомогательными командами на шагах 1.-2. (их
результатов мы дожидаемся и перерабатываем), а команда из шага 3. должна
делаться просто exec. А в моём скрипте такая разница не предусмотрена
оказалась. (Кажется, я сейчас понял, что не сложно переписать это
правильно, особенно как в моём упрощённом случае, когда порядок и
количество команд заранее предопределено.)
***
(Эти два сообщения сохранены как
<http://hub.darcs.net/imz/cuglify/browse/heterogeneous-platform-initial-next-step.md>.)
Модель реализации такой связки с каналом для команд
===================================================
(Скрипты сохранены в <http://hub.darcs.net/imz/cuglify/>.)
`server_dummy.sh` (на месте Process на x86-машине; вызывается, когда
нужны результаты его работы):
#!/bin/bash
set -ex
readonly PORT="$1"
shift
try_send_cmd() {
# in a format suitable for xargs:
for w in "$@"; do echo "$w"; done | nc 0 "$PORT"
}
# Connection may (temporarily) fail in any case!
#
# (In case of local ideal connections, there still is a similar
# problem: the listening side may not be listening yet for a short
# period of time after it handled the previous connection.)
send_cmd() {
while ! try_send_cmd "$@"; do :; done
}
send_cmd a.i
send_cmd echo a
sed 's:a:b:'
exec <&- >&-
send_cmd cat a.i
Простые helpers (для реакции на стороне клиента на команды от сервера;
ради того, чтобы быстро слушать порт дальше после принятия команды --
&):
`bg_exec`:
#!/bin/sh
exec "$@" &
`bg_cat_fromOUT_to`:
#!/bin/bash
exec cat <&101- > "$1" &
`bg_exec_toIN`:
#!/bin/bash
exec "$@" >&100- &
`client.sh` (в роли distcc там, где собираем):
#!/bin/bash
set -ex
# Preparation:
# The pipe with Process (run on server-side):
rm -fv Process_{in,out}.pipe
mkfifo Process_{in,out}.pipe
# Overall, we were asked (by Makefile etc.):
# gcc -c a.c -o a.o
# and we pass the args to Process,
# because it decides what to do.
# The listeners for commands (are run in parallel):
{
exec 100>Process_in.pipe
exec 101<Process_out.pipe
# 0. Get the filename (say, a.i) to save the output of Process to:
nc -l 9876 | xargs ./bg_cat_fromOUT_to 100>&-
exec 101<&-
# 1. Instead of running the above single command,
# we get the result of (via stdout instead of a.orig.i):
# gcc -E a.c [-o a.orig.i]
# as asked by:
nc -l 9876 | xargs ./bg_exec_toIN
exec 100>&-
# 2. ...And send it to Process
# (or gcc in the case of distcc):
# ...(through Process_in.pipe)
# which sends the result back
# (either a.o in the case of `distcc`
# or a.i in the case of Process):
# ...(through Process_out.pipe or a.i directly)
# 3. And we are asked to run on it (saved as a.i):
# gcc -c a.i -o a.o
nc -l 9876 | xargs ./bg_exec
} &
# Running Process (or real gcc in case of distcc):
exec ./server_dummy.sh 9876 arg1 arg2 <Process_in.pipe >Process_out.pipe
--
Best regards,
Ivan
Подробная информация о списке рассылки Devel