Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Пользовательский интерфейс для создания многомодульных программ #87

Closed
Mazdaywik opened this issue Mar 18, 2017 · 17 comments
Assignees
Labels

Comments

@Mazdaywik
Copy link
Member

Mazdaywik commented Mar 18, 2017

Эта задача — подзадача #76 и ответ на #86.

В этой задаче описывается расширения пользовательского интерфейса и поведения программ srefc-core, srmake-core, srefc, srmake, необходимые для создания многомодульных программ, предполагаемых в рамках задачи #76.

Будем говорить, что компилируемое приложение содержит нативный код, если

  • среди единиц трансляции на входе присутствуют пары .rasl+.cpp (библиотечные файлы, файлы рантайма),
  • либо в исходных текстах имеются нативные вставки,
  • либо включена оптимизация прямой кодогенерации.
    Любой из вышеперечисленных случаев вынуждает использовать компилятор C++ для создания двоичного модуля целевой платформы.

Пользовательский интерфейс и поведение srefc-core

Начнём с простого.

Новые параметры командной строки

Компонент srefc-core получает следующие параметры командной строки:

  • --prefix=… (-p) — указывает префикс, добавляемый в начало файла, необязательный параметр. Используется как при создании исполнимых файлов, так и при создании библиотек (при этом должен быть указан правильный префикс.
  • --reference=… (-r) — добавляет ссылку на N- или R-модуль, автоматически загружаемый при запуске приложения.
  • --exesuffix=…, --libsuffix=… — заменяют --targsuffix=…, используемый сейчас (1.λ.11.2). Значения по умолчанию .EXE и .DLL, соответственно. Далее на значения, заданные этими ключами будем ссылаться как (EXESUF) и (LIBSUF).
  • --makeexe (-x), --makelib (-l), -R — взаимоисключающие, предписывают собирать либо модуль типа I, либо модуль типа N или R, либо модуль типа R. По умолчанию используется опция --makeexe.
  • --no-dot-native — отключает создание файла .native.(LIBSUF).
  • --incorporated=libname — указать, что библиотека с именем libname будет встроена в текущий собираемый файл.

Детали поведения

Префикс ищется по тем же путям поиска, что и исходные файлы. Файл префикса должен иметь расширение .exe-prefix либо .lib-prefix, однако в командной строке расширение явно не задаётся, а определяется на основе опций --makeexe или --makelib.

Ссылки на библиотеки при компиляции не ищутся, а просто записываются в файл.

Если задан префикс и при этом приложение содержит нативный код, то помимо исполнимого файла с именем (TARGET)(EXESUF) также будет по умолчанию создаваться файл (TARGET).native(LIBSUF). Если указан ключ --no-dot-native, то при наличии нативного кода на входе компиляция будет прерываться с выдачей сообщения об ошибке.

Опция --makelib при явно указанном префиксе и/или при наличии нативного кода создаёт модуль типа N, в противном случае — модуль типа R. Если в единицах трансляции есть нативный код, то с опцией --makelib в командную строку компилятора C++ добавляется ключ -DREFAL_5_LAMBDA_MAKELIB. Таким образом можно иметь общий рантайм для I+- и N+-модулей.

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

Возникает вопрос: а какое расширение должно быть у файла, если указана опция -R и пользователь явно не задал имя целевого файла? По-хорошему, оно должно быть .rasl, но тогда оно будет совпадать с именем выходного файла для первой единицы трансляции. Тогда получаем два варианта: или изменить имена выходных файлов (например, на .raslo или .orasl), или использовать расширение целевого файла .out.rasl или .rldl/.srdl (Refal-5 Lambda Dynamic Link/Simple Refal Dynamic Link). Ответ на этот вопрос будет найден позже.

В качестве префикса может должен использоваться уже готовый модуль типа I. следовательно, srmake-core должен уметь отличать «голые» exe-шники от модулей типа I с RASL-кодом и не добавлять дополнение до 4096 в последние. Или добавлять таким образом, чтобы рантайм мог его проигнорировать. Предпочтительнее первый вариант. Таким образом, даже если префикс содержит только рантайм (без стандартных библиотек), он обязан иметь дополнение до 4096.

Опция --incorporated является антонимом опции --reference: если в запущенной программе присутствует модуль с записью «incorporated» (нужно будет добавить в формат RASL), то при наличии в других модулях записи REFERENCE с тем же именем, последняя будет игнорироваться, поиск соответствующего модуля будет подавляться. «Инкорпорирование» библиотек полезно для случая, когда используются «толстые» префиксы, содержащие несколько библиотек внутри.

Пользовательский интерфейс и поведение srmake-core

Новые параметры командной строки

Компонент srmake-core получает опции --prefix (-p), --reference (-r), -R, --makeexe (-x), --makelib (-l), -R с тем же смыслом, что и srefc-core.

Детали поведения

Тут уже интереснее. Утилита srmake-core, как мы помним, ищет единицы трансляции по путям поиска и формирует командную строку для srefc-core. Если единица трансляции уже скомпилирована, то ищется файл с расширением .rasl.froms и анализируется также, как и исходный текст (в поисках комментариев //FROM). Предлагается модифицировать это поведение следующим образом.

В список искомых файлов добавляются файлы, указанные в ключах --prefix и --reference (при этом расширение префиксов определяется ключами --makeexe и --makelib (в случае ключа -R префиксы игнорируются). Это раз. Префиксы и ссылки также могут содержать списки //FROM, а значит и иметь зависимость от других файлов. Для префиксов это немножко странно, но, почему бы и нет.

Помимо комментариев //FROM, srmake-core начинает понимать комментарии //PREFIX и //REFERENCE с очевидным смыслом. При этом, если заданы различные префиксы в командной строке и в одном из файлов, либо разные префиксы в двух файлах, происходит ошибка компиляции. При этом одно и то же имя префикса может быть указано в разных местах одновременно, это ошибкой не является.

Поскольку помимо ссылок //FROM поддерживаются и другие типы ссылок, расширение .rasl.froms меняется на расширение .rminfo .rasl.rminfo (Refal Make info).

Общий взгляд на компиляцию и сборку, изменения srefc и srmake

Что дают новые возможности srmake-core (и srefc-core)

Как можно видеть, новые возможности srmake позволяют стандартные Library.sref и LibraryEx.sref либо скомпилировать в отдельный N-модуль, либо даже вкомпилировать (инкорпорировать) в префикс. В обоих случаях нужно будет создать файлы, соответственно, Library.rminfo и LibraryEx.rminfo, содержащие, соответственно, тег //REFERENCE либо //PREFIX. В итоге, код, использующий //FROM LibraryEx, по-прежнему останется компилируемым.

Но при этом нужно пользователю предоставить свободу выбора: компилировать программу в модуль типа I+, используя исходные тексты рантайма и библиотек, компилировать программу, используя «толстый» готовый префикс с рантаймом и всеми стандартными библиотеками (Library, LibraryEx, Hash), используя «лёгкий» префикс с одним только рантаймом (если стандартные библиотеки не нужны или должны подгружаться отдельно).

Новые ключи srefc и srmake

В текущей версии скрипты srefc и srmake дословно передают свои ключи соответствующей программе ***-core, но теперь они будут по особому обрабатывать свой первый аргумент.

Итак, если командная строка начинается с одного из следующих слов, то оно обрабатывается скриптом, но при этом не передаётся нижележащей программе:

  • --rich — добавить в пути поиска префикс с инкорпорированными стандартными библиотеками и файлы *.rminfo для библиотек, ссылающиеся на префикс. Используется по умолчанию.
  • --slim — добавить в пути поиска префикс, состоящий только из одного рантайма и стандартные библиотеки, скомпилированные в один или несколько N-модулей.
  • --scratch — добавить в пути поиска единицы трансляции рантайма и стандартных библиотек (.rasl или .rasl+.cpp) без собранных префиксов и N-модулей. Эта опция даёт возможность пользователю создавать свои модули типа I+ и N+, либо компилировать с оптимизацией прямой кодогенерации без лишних файлов .native.(LIBSUF).

Критерий завершённости

Эта задача будет закрыта, когда будет реализована большая часть описанной функциональности. Программа-минимум: весь интерфейс, модули типа I0, модули типа R. Программа-максимум: весь интерфейс, модули типа I0, модули типа R, модули типа N — полная функциональность #76.

@Mazdaywik
Copy link
Member Author

Уточнение поведения утилиты srefc-core

Проблема

В предложенной спецификации, если среди единиц трансляции есть нативный код, и при этом явно задан префикс, то создаётся библиотека (TARGET).native(LIBSUF), содержащая нативный код. Данный подход мог бы работать, но противоречит с общей идеей.

Общая идея такова.

  • Либо собирается целевой файл из префикса и чистого RASL’а, либо из единиц трансляции, среди которых есть рантайм в виде нативного кода.
  • С точки зрения утилит srefc-core и srmake-core рантайм явно не выделяется — он является одной из единиц трансляции, которая просто содержит нативный код.
  • Для srefc* рантайм надо указывать явно, для srmake* — он находится через комментарии //FROM (по умолчанию //FROM refalrts есть в Library.sref).
  • Таким образом, рантайм совершенно независим от компилятора — программист волен компоновать сгенерированный код с любой версией рантайма с любым его именем.
  • Рантайм должен компоноваться статически, т.е. программы на Простом Рефале не должны явно зависеть от какой-нибудь refalrts.dll или refalrts.so, поскольку написание такой библиотеки (а) делает программы зависимыми от библиотеки, (б) затруднительно при реализации — нужно обеспечить её сборку и компоновку для зоопарка компиляторов на Windows. При реализации N-модулей предполагается использовать узкое горлышко — единственную экспортируемую функцию, через которую передаётся указатель на структуру с указателями на фактические функции рантайма, что должно быть проще.
  • Из предыдущего пункта следует, что каждый N-модуль должен содержать свой экземпляр рантайма для динамической библиотеки.
  • Да и с динамически линкуемым рантаймом, когда в refalrts.h находятся имена функций, непосредственно импортируемые из DLL, всё равно потребуется заглушка для Windows в виде obj- или lib-файла, содержащего таблицу импорта, причём для каждого компилятора своя.

Но библиотека (TARGET).native(LIBSUF) сама должна содержать рантайм (поскольку нативные функции будут вызывать функции рантайма) и по сути будет являться N+-модулем, который содержит только нативные функции (или все функции с -Od). А значит, ему для компоновки потребуется рантайм, который нужно как-то прокидывать. Или явно указывать особым ключом имя рантайма, или придумать какой-то иной хак.

Решение

Если префикс явно задан и единицы трансляции на входе содержат нативный код, то фиксируется ошибка компиляции.

Если программисту действительно хочется использовать этот префикс и собрать компоненты с этим нативным кодом, то тут два варианта:

  • Создать отдельную N+-библиотеку, создать программу из префикса и этой библиотеки (в тривиальном случае это может быть файл без кода и с комментарием //REFERENCE …),
  • Собрать программу из исходников префикса (если они доступны).
    Первый вариант по смыслу эквивалентен (TARGET).native(LIBSUF).

@Mazdaywik
Copy link
Member Author

Mazdaywik commented May 21, 2017

Уточнение по поводу srmake

А зачем у префиксов и ссылок нужно анализировать зависимости? Ведь они уже готовые скомпилированные элементы, их не надо перекомпилировать.

Ссылка или префикс требуют для своего подключения каких-то дополнительных исходников? Если да, то почему тогда эти исходники не были скомпилированы в саму библиотеку или префикс? Можно пост-фактум скомпилировать и приписать RASL. Можно вместо одной библиотеки создать другую, которая будет ссылаться на первоначальную.

Если ссылка или префикс должны работать вместе с исходниками, то, может лучше, сам исходник указать в директиве //FROM, а в исходнике указать //PREFIX или //REFERENCE?

Префикс или ссылка требует другой ссылки? Это разрешается динамически при загрузке. Ссылка требует префикса? См. выше (пустой исходник с двумя комментариями //PREFIX и //REFERENCE).

Так что если возникнет ситуация подключать конгломерат из исходника и ссылки/префикса, лучше подключать через доступ к исходнику. Недостаток: требуется править исходные тексты, добавляя в них //FROM для конгломерата. Непосредственно из командной строки задать дополнительные исходники нельзя (исходя из идеологии srmake). Но, если на практике возникнет потребность задавать для srmake несколько исходников на входе, возможно, они будут как-то задаваться.

Mazdaywik added a commit that referenced this issue May 21, 2017
Что изменилось в srmake-core:
* Во-первых, понимаются все новые опции командной строки.
* Во-вторых, извлекаются из файлов ключи //PREFIX и //REFERENCE.
* В-третьих, правильным образом формируется командная строка для srefc-core
  (который на данном этапе игнорирует новые опции).

Ссылки на задачи #89 и #90 добавлены, поскольку в них предполагалось
реализовать, соответственно, //PREFIX и //REFERENCE.
Mazdaywik added a commit that referenced this issue May 21, 2017
На данный момент дерево исходников использует старый интерфейс командной
строки, в то время как уже реализован более новый (#88, #87). Данная фиксация
обновляет стабильную версию чтобы иметь возможность обновить скрипты
на новые опции командной строки.
@Mazdaywik
Copy link
Member Author

Уточнение.

В дополнение к опциям -x, -l, -R для утилит srefc-core и srmake-core следует добавить также и -C (--compile-only), предписывающую только откомпилировать исходники без запуска компилятора C++.

Сейчас такое поведение поддерживается за счёт неуказания ключей --cpp-command-exe или --cpp-command-lib (в зависимости от того, в каком режиме выполняется компиляция), но идеологически это должен быть особый режим.

А случай с неуказанием компилятора должен вполне корректно работать в режиме -R.

Mazdaywik added a commit that referenced this issue Jul 3, 2017
Ранее ключ -C использовался для задания опций командной строки компилятора C++,
но, поскольку у него ещё были синонимы -F и --cppflags, я его оттуда убрал.
В старом смысле ключ -C использовался в src/make.*, поменял на -F.
@Mazdaywik
Copy link
Member Author

Очередные уточнения

Что имеем сейчас

Что работает

Сейчас компилятор умеет правильно собирать модули I0 и I+. Поддерживаются три режима: --slim (по умолчанию), --rich и --scratch. Имеются также три стандартные библиотеки: Library, LibraryEx и Hash

  • Режим --slim создаёт исполнимые файлы с префиксом-интерпретатором, который содержит скомпилированные библиотеки Library и Hash. Библиотека LibraryEx статически прилинковывается при её использовании.

  • Режим --rich создаёт исполнимые файлы с префиксом-интерпретатором, который содержит все три стандартные библиотеки.

  • Режим --scratch создаёт исполнимые файлы без префикса. Для создания префикса используется компилятор C++. Все три библиотеки прилинковываются статически.

  • Библиотека Library содержит все встроенные функции Рефала-5 и некоторые другие функции-расширения. Большинство этих функций написано на C++.

  • Библиотека Hash содержит единственную функцию HashLittle2-Chars, вычисляющую хэш-функцию Дженкинса. Функция написана на C++.

  • Библиотека LibraryEx содержит полезные функции-расширения, которые пишутся на Рефале.

Разделение стандартной библиотеки на Library и LibraryEx историческое. До появления нативных вставок (#11) библиотека Library представляла собой файл на C++, а библиотека LibraryEx была написана на Рефале.

Режимы --slim и --rich позволяют компилировать программы не используя компилятор C++. Ограничение: нативный код (см. выше) в программе должен отсутствовать. Для компиляции программ с нативным кодом используется режим --scratch.

Различие (на данный момент) между --slim и --rich в том, что во втором библиотека LibraryEx может быть скомпилирована в режиме оптимизации прямой кодогенерации, что автоматически даёт более эффективные программы.

Что не работает

Есть частичная поддержка сборки библиотек (модулей типа N), но они пока собираются точно также, как модули типа I: используется только другой префикс командной строки (который сейчас установлен в пустую строку)

В чём проблема

Проблема в подходе к созданию библиотек. Вернее, проблем несколько.

Разная компоновка библиотек для модулей типа I и N

Допустим, мы компилируем программу, использующую библиотеки Library и LibraryEx в режиме --slim. У нас получится исполнимый файл, префикс которого уже содержит Library, к нему статически припишется LibraryEx и сами пользовательские файлы.

Если точно также мы соберём библиотеку, то… А что тогда мы бы хотели получить? Библиотеку Library помещать в префикс нельзя. Потому что тогда будут две копии внутренних переменных библиотеки, в частности таблицы открытых файлов и эти таблицы между собой будут несовместимы. Поэтому префикс должен, по-хорошему, содержать ссылку на Library. Библиотеку LibraryEx технически можно прилинковывать статически, поскольку глобальных переменных она (пока) не содержит. Но статическая линковка вступает в конфликт с сутью разделяемых библиотек — не дублировать код, а пользоваться общим экземпляром. Поэтому неплохо было бы библиотеку LibraryEx подключать тоже по ссылке.

(С другой стороны, ничто не мешает подключать LibraryEx по ссылке и в I-модули. В этом случае библиотека должна быть разделяемым файлом.)

Получается, что для разных типов целевого файла (I и N) библиотеки должны подключаться по-разному. Разница в компоновке Library осуществляется просто за счёт использования разных префиксов (что уже поддерживается). Разницу в компоновке LibraryEx нужно будет как-то реализовывать.

А что, если мы хотим скомпилировать эту библиотеку в модуль R? Почему бы и нет, ведь если мы создаём программу с префиксом, то нативного кода в ней точно нет. По-хорошему, этот модуль R также должен иметь ссылки на Library и LibraryEx. Но как, ведь для таких модулей не создаются префиксы?

Подключение рантайма

Чтобы в режиме --scratch создать исполнимый модуль, нужно иметь не только исходные файлы на Рефале, но и файлы рантайма. Библиотека Library содержит ссылку на refalrts, refalrts содержит ссылки на свои подкомпоненты. Поэтому программы, которые используют Library, нормально собираются и работают. А вот написать программу, которая Library не использует, уже в режиме --scratch так просто не получится: придётся явно указывать в одном из файлов, что она зависит от рантайма.

Это костыль.

Более того, для I+ модуля и N+ модуля нужны разные рантаймы. Выше предлагалось использовать опцию -DREFAL_5_LAMBDA_MAKELIB, что тоже костыль. Нужен более гибкий подход.

Предлагаемое решение

Предлагается изменить алгоритм сканирования путей поиска файлов. Сейчас последовательно просматриваются все папки, для каждой папки (назовём её DIR) ищется файл (назовём его FILE) — просто строится путь DIR/FILE. Предлагается для исполнимых файлов для каждой папки заглядывать сначала в DIR/exe/FILE, потом в DIR/FILE, для библиотек аналогично DIR/lib/FILE, затем DIR/FILE. Таким образом, можно иметь разные варианты библиотек для компиляции I- и N-модулей.

Предлагается изменить подход к рантайму. Рантайм для srmake-core должен явно указываться опцией командной строки, например --runtime=refalrts. Он будет считаться очередным исходником, который ищется стандартным алгоритмом. В сочетании с предыдущим предложением можно будет иметь разные рантаймы в подпапках exe/refalrts и lib/refalrts.

Преимуществом префикса --rich перед --slim была названа возможность компиляции библиотеки LibraryEx с оптимизацией прямой кодогенерации. Режим --slim ограничен тем, что все подключаемые библиотеки не должны иметь нативного кода. Если скомпилировать LibraryEx в динамически загружаемую библиотеку, то это преимущество --rich пропадает: библиотеку можно скомпилировать в N-модуль и подгружать по ссылке. Другое преимущество, тем не менее, остаётся: единый исполнимый файл без свиты библиотек.

Точно также с реализацией динамических библиотек из префикса --slim можно убрать библиотеку Hash как идейно чужеродную, оставив в этом префиксе только интерпретатор.

@Mazdaywik
Copy link
Member Author

Имеет смысл режим --slim разделить на два: --slim-static и --slim-dynamic. В первом случае библиотека LibraryEx будет подключаться статически (т.е. она будет скомпилирована в LibraryEx.rasl), во втором случае — динамически (будет скомпилирована в LibraryEx.rasl-module или даже в LibraryEx.dll/LibraryEx.so в режиме прямой кодогенерации).

Также после реализации N-модулей из префикса --slim нужно будет вынести модуль Hash.

@Mazdaywik
Copy link
Member Author

Mazdaywik commented Sep 5, 2019

Что уже реализовано?

  • Реализована генерация всех типов модулей: I0, I+, R, N (Реализация модулей типа N #170).
  • Есть 5 стандартных библиотек: GetOpt, Hash, Library, LibraryEx, Platform.
  • Библиотеки GetOpt и LibraryEx написаны на Рефале, без состояния (статических ящиков).
  • Библиотека GetOpt содержит функцию GetOpt, разбирающую аргументы командной строки.
  • Библиотека Hash содержит функцию HashLittle2-Chars, реализующую хеш-функцию Дженкинса. Эта библиотека, помимо прочего, скомпилирована в Hash.dll или Hash.so.
  • Библиотека Library — встроенные функции Рефала-5 + дополнительные функции ввода-вывода + функции Platform.
  • Библиотека LibraryEx — утилитарные функции на Рефале вроде чтения/записи файлов целиком, функции высших порядков.
  • Библиотека Platform — особенности операционной системы (разделители папок в путях и т.д.). Добавлена для совместимости с Рефалом-05, по факту все функции написаны на Си++ в Library (и ссылаются на refalrts-platform-specific).
  • Поддерживаются режимы --rich, --rich-debug, --slim, --slim-debug, --scratch.
  • В режимах --rich и --rich-debug префикс для исполнимых файлов содержит все 5 библиотек. .rasl-файлы для каждой из них пустые. Файлы .froms ссылаются на префикс.
  • В режимах --slim и --slim-debug префикс содержит только Library. Для библиотеки Hash имеется файл Hash.rasl, который содержит ссылку динамической загрузки. Остальные библиотеки скомпилированы непосредственно в .rasl.
  • В режиме --scratch все библиотеки доступны в виде файлов для компоновки. При обычной компиляции LibraryEx и GetOpt скомпилированы в .rasl, при релизной (обновление дистрибутива, set RELEASE=1) они скомпилированы в .rasl+.cpp. (Файл Platform.ref пустой, библиотеки собираются без прелюдии, поэтому он всегда компилируется в Platform.rasl.)
  • Префиксы режимов --***-debug могут считывать файл @refal-5-lambda-diagnostics.txt, в котором задаются отладочные опции рантайма. Кроме того, доступен встроенный пошаговый отладчик (правда, он не работает).

Что криво?

Перечисленное выше обеспечивает хорошую работу при сборке исполнимых файлов. Но вот с библиотеками криво.

Допустим, мы собираем библиотеку (--makelib), которая использует LibraryEx.

В режиме --rich (--rich-debug) к целевому файлу будет приписан пустой файл LibraryEx.rasl. В итоге в модуле будут неразрешённые ссылки, к примеру, на LoadFile, но при этом не будет никакой информации, откуда их брать.

Если библиотека будет загружаться из модуля, тоже собранного в режиме --rich — никаких проблем не будет.

Если использующий её модуль собран в режиме --slim или --scratch и сам не содержит (статически скомпонованную) LibraryEx, то библиотека не загрузится — обнаружатся неразрешённые ссылки.

Если до этой библиотеки были загружены другие библиотеки, некоторые из них имеют статически скомпонованную LibraryEx, то неразрешённые функции свяжутся с ними. Однако, если потом одна из предшествующих библиотек будет выгружена, получится висячая ссылка.

Теперь посмотрим, что получится, если библиотеку собрать в режиме --slim (--slim-debug). Библиотека LibraryEx скомпонуется статически, причём её функции будут функциями в байткоде. Такая библиотека успешно загрузится из любого исполнимого файла. Проблема тут будет скорее идеологическая (эстетическая) — в памяти может быть несколько экземпляров LibraryEx, что (как сказано в предыдущем комментарии) противоречит самой идее динамически загружаемых библиотек.

В режиме --scratch библиотека LibraryEx также статически прикомпонуется, причём, скорее всего, будучи скомпилированной в режиме прямой кодогенерации.

Как предлагается это исправить?

  • Для всех библиотек (LibraryEx, GetOpt, Platform, возможно, Library для единообразия) создавать библиотеки динамической компоновки по аналогии с Hash.
  • К скомпилированным библиотекам в режиме --scratch добавлять --incorporated=… с именем самой же себя. Это автоматически обеспечит добавление псевдонимов к префиксам --rich (--rich-debug) и --slim (--slim-debug).
  • Режим --slim (--slim-debug) делим на два: --slim-static (--slim-static-debug) и --slim-dynamic (--slim-dynamic-debug). В первом режиме библиотеки LibraryEx и GetOpt компонуются статически, во втором — динамически. Режим --slim по умолчанию должен быть статическим для исполнимых файлов и динамическим для библиотек.
  • Режим --rich. Пустые .rasl-файлы для библиотек в папке exe (как сейчас, только в подпапке), файлы с ссылками в папке lib. Соответственно, получаем толстые исполнимые файлы со всеми стандартными библиотеками и тонкие библиотеки.

@Mazdaywik
Copy link
Member Author

Вместо --slim-static и --slim-dynamic нужно использовать просто --static и --dynamic.

@Mazdaywik
Copy link
Member Author

Что ещё криво?

Криво то, что опция --makelib не обеспечивает должной инкапсуляции. В режимах --rich и --slim программы без нативного кода компилируются в R-модуль, с нативным кодом — в кривой N-модуль, поскольку библиотечный рантайм не будет прилинкован. В режиме --scratch программы с нативным кодом скомпилируются в корректный N-модуль, программы без нативного кода — тоже в корректный N-модуль, однако префикс у них будет пустым.

Что делать?

Ничего. Это не бага, а фича. Её исправление сильно всё усложнит и потребует много времени.

@Mazdaywik
Copy link
Member Author

Mazdaywik commented Sep 22, 2019

--scratch и --make-lib

Оговорки про --slim касаются и --scratch: должна быть возможность собрать программу из исходников, но при этом с динамическими библиотеками.

Соответственно, нужны два режима: --scratch-static и --scratch-dynamic. По умолчанию --scratch компонует статически для исполнимых файлов и динамически для библиотек.

  • --scratch-static и --scratch-dynamic.

@Mazdaywik
Copy link
Member Author

Как реализовать --***-static и --***-dynamic (где *** — scratch и slim)?

Для --slim всё просто: в srlib/slim/exe должны находиться скомпилированные в RASL библиотеки, в srlib/slim/lib — .rasl’ы с ссылками. Путь поиска — srlib/slim, подпапки exe и lib обнаружатся сами.

Для --slim-static и --slim-dynamic можно воспользоваться той же папкой srlib/slim, но пути поиска настроить, соответственно, srlib/slim/exe;srlib/slim и srlib/slim/lib;srlib/slim.

Для режима --scratch немного сложнее: в нём уже есть подпапки exe и platform-…/lib для файлов рантайма. Решение: разделить файлы рантайма и библиотек в разные папки (для последних выделить scratch-lib, например). Папку библиотек реализовать как описано выше.

@Mazdaywik
Copy link
Member Author

Mazdaywik commented Oct 21, 2019

Много переменных

Компоновка определяется большим числом переменных: префикс (тонкий/толстый/нет), тип компоновки стандартных библиотек (авто/статический/динамический), наличие отладочного кода (есть/нет, см. #261). Компиляция также определяет целевой файл: исполнимый, DLL или R-модуль. Все эти --slim-static, --slim-debug, --slim-static-debug… — путь в никуда, комбинаторный взрыв вариантов.

Предлагается добавить к скриптам сборки три набора ключей (первый — по умолчанию):

  • Префикс:
    • --slim (по умолчанию) — тонкий префикс, не содержащий LibraryEx, Hash и GetOpt.
    • --rich — толстый префикс со всеми стандартными библиотеками,
    • --scratch — префикса нет, требуется использовать компилятор Си++.
  • Связывание стандартных библиотек:
    • --auto — статическое для исполнимых файлов, динамическое для библиотек,
    • --static,
    • --dynamic.
  • Средства отладки и профилирования:
    • --debug — они есть (для режима --scratch неявно подключается refalrts-diagnostic-initializer),
    • --no-debug.

Поскольку это ключи скриптов, они должны располагаться в начале командной строки.

Нужно продумать разумное поведение для любой комбинации этих ключей (включая ключи компилятора --makeexe, --makelib и -R). Разумное — по принципу наименьшего удивления.

Надо иметь ввиду, что R-модули не всегда используются как библиотеки, они могут быть готовыми программами для интерпретатора (#48), который их загружает и вызывает функцию Go.

@Mazdaywik
Copy link
Member Author

Mazdaywik commented Oct 21, 2019

Префикс Связывание Отладка Режим Случай
1 --slim --auto --debug -x A
2 --slim --auto --debug -l Б
3 --slim --auto --debug -R Б
4 --slim --auto --no-debug -x В
5 --slim --auto --no-debug -l Б
6 --slim --auto --no-debug -R Б
7 --slim --static --debug -x А
8 --slim --static --debug -l Г
9 --slim --static --debug -R Г
10 --slim --static --no-debug -x В
11 --slim --static --no-debug -l Г
12 --slim --static --no-debug -R Г
13 --slim --dynamic --debug -x Д
14 --slim --dynamic --debug -l Б
15 --slim --dynamic --debug -R Б
16 --slim --dynamic --no-debug -x Е
17 --slim --dynamic --no-debug -l Б
18 --slim --dynamic --no-debug -R Б
19 --rich --auto --debug -x Ё
20 --rich --auto --debug -l Ж
21 --rich --auto --debug -R З
22 --rich --auto --no-debug -x И
23 --rich --auto --no-debug -l Й (= Ж?)
24 --rich --auto --no-debug -R З
25 --rich --static --debug -x Ё
26 --rich --static --debug -l Ж
27 --rich --static --debug -R З
28 --rich --static --no-debug -x И
29 --rich --static --no-debug -l Й (= Ж?)
30 --rich --static --no-debug -R З
31 --rich --dynamic --debug -x Ё
32 --rich --dynamic --debug -l Ж
33 --rich --dynamic --debug -R З
34 --rich --dynamic --no-debug -x И
35 --rich --dynamic --no-debug -l Й (= Ж?)
36 --rich --dynamic --no-debug -R З
37 --scratch --auto --debug -x К
38 --scratch --auto --debug -l Л
39 --scratch --auto --debug -R М!
40 --scratch --auto --no-debug -x Н
41 --scratch --auto --no-debug -l О
42 --scratch --auto --no-debug -R М!
43 --scratch --static --debug -x К
44 --scratch --static --debug -l П
45 --scratch --static --debug -R М!
46 --scratch --static --no-debug -x Н
47 --scratch --static --no-debug -l Р
48 --scratch --static --no-debug -R М!
49 --scratch --dynamic --debug -x С
50 --scratch --dynamic --debug -l Л
51 --scratch --dynamic --debug -R М!
52 --scratch --dynamic --no-debug -x Т
53 --scratch --dynamic --no-debug -l Н
54 --scratch --dynamic --no-debug -R М!

Случай A

Строится исполнимый файл с тонким префиксом (Library, Platform) и отладочными средствами, библиотеки LibraryEx и GetOpt компонуются статически в виде RASL’а, библиотека Hash линкуется динамически.

Случай Б

Строится R-модуль без префикса, все библиотеки компонуются динамически.

Случай В

Строится исполнимый файл с тонким префиксом (Library, Platform) и без отладочных средств, библиотеки LibraryEx и GetOpt компонуются статически в виде RASL’а, библиотека Hash линкуется динамически.

Случаи А и В отличаются лишь отладочными средствами.

Случай Г

Строится R-модуль без префикса, библиотеки Library, Platform и Hash компонуются динамически, LibraryEx и GetOpt — статически.

Случай Д

Строится исполнимый файл с тонким префиксом (Library, Platform) и отладочными средствами, библиотеки LibraryEx, GetOpt и Hash линкуются динамически.

Случай Е

Строится исполнимый файл с тонким префиксом (Library, Platform) и без отладочных средств, библиотеки LibraryEx, GetOpt и Hash линкуются динамически.

Случаи Д и Е отличаются только отладочными средствами.

Случай Ё

Строится исполнимый файл с толстым префиксом (Library, Platform, LibraryEx, Hash, GetOpt) и отладочной информацией.

Случай Ж

Строится динамическая библиотека с толстым префиксом (Hash, LibraryEx, GetOpt), скомпилированным с отладочными средствами (?), библиотеки Library и Platform компонуются статически.

Файлы библиотек Hash.rasl, LibraryEx.rasl и GetOpt.rasl содержат ссылки. Поскольку префикс содержит их же, но инкорпорировано, то они разрешаются по дефолту. Они нужны для случая З.

Таким образом, все файлы библиотек содержат ссылки.

Случай З

При компиляции в режиме -R компилятор префикс не добавляет. Все библиотеки подключаются динамически. Отличие от Б в том, что в командной строке будет наличествовать префикс, но он будет игнорироваться.

Поэтому все библиотеки для случаев Ж и З должны содержать ссылки не смотря на наличие толстого префикса.

Случай И

Строится исполнимый файл с толстым префиксом (Library, Platform, LibraryEx, Hash, GetOpt), без отладочной информации.

Случаи Ё и И отличаются только отладочной информацией.

Случай Й

Строится динамическая библиотека с толстым префиксом (Hash, LibraryEx, GetOpt), скомпилированным без отладочных средств (?), библиотеки Library и Platform компонуются статически.

Файлы библиотек Hash.rasl, LibraryEx.rasl и GetOpt.rasl содержат ссылки. Поскольку префикс содержит их же, но инкорпорировано, то они разрешаются по дефолту. Они нужны для случая З.

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

Случай К

Все библиотеки скомпилированы в RASL и Си++ (если использовалась -Od), с отладочной информацией (--markup-context), доступны файлы рантайма для исполнимого модуля, к программе прилинковываются диагностические средства (#261). При компиляции получается исполнимый файл, к которому статически прилинковываются библиотеки.

При использовании srmake всё получается просто и понятно — библиотеки находятся по зависимостям. Для srefc все зависимые файлы (состав которых может меняться от версии к версии) приходится указывать вручную.

Случай Л

Библиотеки прилинковываются динамически — доступны только .rasl’ы с ссылками. Рантайм доступен только библиотечный. Средства диагностики в нём используются (например, не отключены макросами препроцессора, см. #261). При компиляции получается библиотека, которая динамически загружает стандартные библиотеки.

Про srefc и srmake та же оговорка, что и для случая К.

Случай М!

Восклицательный знак символизирует ошибку, т.е. эта комбинация не поддерживается.

Если используется srefc и среди исходников нет файлов с нативным кодом, то R-модуль успешно соберётся (см. случай Б).

Если использовать srmake, то в командную строку добавится рантайм динамических библиотек и мы получим ошибку компиляции (линковки).

Случай Н

Библиотеки скомпилированы в RASL и Си++ (если использовалось -Od), без отладочной информации, доступны файлы рантайма исполнимого модуля, недоступны средства диагностики. При сборке получается исполнимый файл, не содержащий средств диагностики, стандартные библиотеки компонуются статически.

Отличие от случая К — в отсутствии диагностики, остальное то же самое.

Случай О

Стандартные библиотеки прилинковываются динамически. Доступен библиотечный рантайм, но без средств диагностики. При компиляции получается динамическая библиотека, которая динамически подгружает стандартные библиотеки.

Случай О аналогичен случаю Л, только средства диагностики в нём отсутствуют.

Случай П

Библиотеки LibraryEx, Hash и GetOpt скомпилированы в RASL и Си++, доступны средства рантайма для библиотек и диагностические средства. Библиотеки Platform и Library подключаются динамически. При сборке получается динамическая библиотека, к которой статически прилинкованы некоторые стандартные библиотеки (понятно, что только те, которые в ней используются).

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

Случай Р

Аналогичен случаю П, только средства диагностики отключены.

Случай С

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

Случай Т

Аналогичен случаю С, только диагностические средства отсутствуют.

Случаи С и Т — единственные два способа создать исполнимый файл, который библиотеку Library подтягивает динамически. Готового «сверхлёгкого» префикса в компиляторе не предусмотрено.

А как собрать Library.dll?

Для этого Library.rasl+Library.cpp нужно статически прилинковать к библиотечному рантайму, а такого выбора выше нет.

Нужно или разрешать в случаях П/Р линковать Library статически, или придумывать лазейку специально для сборки библиотеки (например, использовать полные пути).

Mazdaywik added a commit that referenced this issue Jan 31, 2020
Ранее .cpp-файлы рантайма для раскрутки брались из /srlib, сейчас берутся
из /lib.

Для сборки отладочных префиксов требуется указывать --squash и -debug,
т.е. переменная SCRIPT_FLAGS может содержать пробелы. Скрипт make.bat
ранее не поддерживал эту функцию.
Mazdaywik added a commit that referenced this issue Jan 31, 2020
Уже не используются
• src/lib-rich-prefix-debug
• src/lib-rich-prefix
• src/lib-rich-prefix-lib
• src/lib-slim-debug-prefix
• src/lib-slim-prefix
Mazdaywik added a commit that referenced this issue Jan 31, 2020
В зависимости от имени файла скрипта реализуется логика srefc или srmake.
Mazdaywik added a commit that referenced this issue Jan 31, 2020
Они теперь копируются из одного шаблона srefc-srmake.*.
Mazdaywik added a commit that referenced this issue Jan 31, 2020
Команда shift для Windows сдвигает аргументы, включая %0, из-за чего
проверка на имя скрипта не работала. Решение: %~n0 вынесено в переменную
до сдвигов. Аналогично модифицирован скрипт для Bash для симметрии.
Mazdaywik added a commit that referenced this issue Jan 31, 2020
После формирования новой папки /lib взамен /srlib скрипты make.*
из lib-dynamic стали формировать библиотеки, содержащие только ссылку
на самих себя. Поэтому теперь при компиляции указывается точный путь
до исходников в папке src/lib вместо одного имени.

В src/make.bat почему-то использовалось только имя файла исходника
без пути (%~n3). Возможно, это когда-то имело смысл, но сейчас утратило
актуальность.

Правки были вынесены в отдельный коммит ради большей ясности.
Mazdaywik added a commit that referenced this issue Jan 31, 2020
Во-первых, он адаптирован к новой папке lib вместо srlib.
Во-вторых, в дистрибутив теперь добавляется папка docs.
Mazdaywik added a commit that referenced this issue Jan 31, 2020
В папке lib/scratch/exe лежат LibraryEx, Hash, GetOpt, в папке
lib/scratch-rt/exe лежат Library и Platform.

Переменные CSOURCES и RSOURCES более не нужны, поэтому удалены.
Mazdaywik added a commit to bmstu-iu9/simple-refal-distrib that referenced this issue Jan 31, 2020
• Опция --ref5rsl добавляет в путь поиска переменную окружения REF5RSL.
• Сгенерированные файлы .rasl и .cpp помещаются по умолчанию в текущую папку,
  альтернативную папку можно указать при помощи --temp-dir=… (эта опция ранее
  поддерживалась, но игнорировалась).
• Работает опция --keep-rasls, ранее она игорировалась.
  По умолчанию .rasl-файлы удаляются после перекомпиляции.
• Работает опция --overwrite-exist-rasls, ранее она игнорировалась.
  По умолчанию существующие .rasl-файлы с теми же именами перезаписываются.
• ИСПРАВЛЕНО: формирование имени целевого файла. Ранее оно могло иметь вид
  sourcefile@1.exe в некоторых редких случаях.
• Флаги --cpp-command-exe-suf=…, --cpp-command-lib-suf=…. На Windows они могут
  использоваться для передачи .def-файла для библиотек, на Linux для передачи
  дополнительных внешних библиотек (вроде -lm, -ldl).
• В шаблоне конфигурации компилятора C++ задаются переменные CPPLINEL,
  CPPLINEESUF, CPPLINELSUF.
• ИСПРАВЛЕНО: в случае успешной компиляции выдаётся сообщение «Compilation
  succeeded» вместо ошибочного «Compilation successed».
• Для поддержки N-модулей изменился API рантайма.
• Поддерживаются N-модули: их генерация и загрузка.
• Для стандартных библиотек Hash, Library, GetOpt, LibraryEx, Platform
  генерируются .dll/.so для динамической загрузки, располагаются в папке /bin.
• Инсталлятор на Windows устанавливает переменную среды RL_MODULE_PATH.
• Небольшие уточнения в README.md.
• Префикс slim(-debug).exe-prefix не содержит библиотеку Hash.
• Расширен аварийный дамп. Помимо поля зрения выдаётся сводка из домена:
  списки загруженных модулей, функций в них и идентификаторов.
• ИСПРАВЛЕНО: предупреждение компилятора GCC на рантайм.
• ИСПРАВЛЕНО: функция Compare выполняется за один виртуальный шаг.
• Вместо папки /srlib используется папка /lib.
• Флаг компилятора --no-sources, позволяющий создать целевой файл
  содержащий только ссылки и/или флаги --incorporated. Без флага
  --no-sources компилятор выдаёт сообщение об ошибке о том, что исходники
  отсутствуют.
• Изменена структура папки /lib в соответствии с задачей
  bmstu-iu9/refal-5-lambda#87
• Префикс rich(-debug).lib-prefix, содержащий LibraryEx, Hash и GetOpt.
• ИСПРАВЛЕНО: компилятор падал при наличии файла filename.cpp при отсутствии
  filename.rasl.
• Модуль может быть сам себе псевдонимом: допустима ситуация, когда модуль
  содержит и псевдоним --incorporated=… и ссылку с тем же именем.
• Скрипты srefc(.bat) и srmake(.bat) поддерживают новые ключи (в соответствии
  с bmstu-iu9/refal-5-lambda#87):
  — ключи --slim, --rich, --scratch определяют префикс или его отсутствие,
  — ключи --static, --dynamic, --auto определяют компоновку с библиотеками
    LirbraryEx и GetOpt: статическая, динамическая и по умолчанию (статическая
    для исполнимых файлов, динамическая для библиотек),
  — ключи --debug, --no-debug включают и отключают компоновку с отладочными
    средствами, по умолчанию отладка отключена.
  Эти ключи должны быть первыми параметрами srefc и srmake, их может следовать
  неограниченное количество, последние имеют приоритет.
@Mazdaywik
Copy link
Member Author

Задача оказалась существенно больше, чем я ожидал ранее. Ортогонализовывать режимы (префикс/отладка/связывание/тип целевого файла) оказалось довольно объёмно и интересно.

Mazdaywik added a commit that referenced this issue Mar 9, 2020
Сборка всего содержимого папки /lib запускается из src/lib/make.*. Ранее
префиксы и динамические экземпляры библиотек собирались отдельно
из src/make.*.

Почти рефакторинг, потому что ранее путь к исходникам библиотек внутри
них прописывался как ../lib/〈библиотека〉, сейчас просто 〈библиотека〉.
Различие мелкое, но оно влияло, в частности, на вычисление cookies.
Mazdaywik added a commit that referenced this issue Mar 9, 2020
Файлы ***-debug-***.ref и ***-***.ref идентичны, поэтому ***-debug-***.ref
не нужны.
Mazdaywik added a commit that referenced this issue Mar 9, 2020
Скрипт src/make.* позволяет собирать проекты с подгонкой опций, например,
для RELEASE=1. Но здесь подгонка невозможна, исходные файлы в этой папке
не содержат кода и содержат только директивы для srmake.

Другой побочный эффект src/make.* — создание подпапки в /build. Но для
сборки префиксов в релизе эта подпапка не нужна, не нужна она для отладки
и в процессе разработки.

Поэтому замена на srmake допустима.
Mazdaywik added a commit that referenced this issue Mar 9, 2020
В файле make.sh устраняются предупреждения ShellCheck. Файл make.bat
переписан параллельно с make.sh.

Относительный путь к корню абстрагирован в переменной ROOT.
Mazdaywik added a commit that referenced this issue Mar 9, 2020
Исходники были перенесены из src/lib-prefixes в src/lib/prefixes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant