Clause
← Уся документація

Перекладено з англійського оригіналу · 1e3a39c8-dirty · 2026-10-26 · Читати англійською

Виконувані файли

Контракт для програм, зібраних через clau -o або збиранням проєкту. Позиційні вхідні файли або рівно одна обрана ціль проєкту компонуються в шлях -o; збирання проєкту компонує кожну обрану ціль-виконуваний файл у її output з маніфесту (компонування, проєкти).

Вибір точки входу

Точка входу — це експортована функція арності 1, що отримує список аргументів. Обрана FUNCTION без FUNCTION/1 натомість може бути експортованою FUNCTION/0: вона виконується без аргументів програми (опції runtime однаково вилучаються). Суфікс /0 або /1 вибирає лише цю арність, тож --entry benchmarks:run/0 запускає run/0, навіть коли run/1 теж експортовано. Типово (без вибору) це завжди main/1.

ДжерелоЗаписОбласть дії
CLI--entry MODULE[:FUNCTION[/ARITY]]Позиційний пакет або єдина обрана ціль проєкту
Маніфестentry = "MODULE[:FUNCTION[/ARITY]]" у таблиці [[targets]]Ця ціль
ТиповоЄдиний escript, інакше єдиний модуль, що експортує main/1Лише коли запитано виконуваний файл (-o або ціль проєкту з output)
ЗбійДіагностика (код виходу 1)
Модуля немає в пакеті<origin>: entry module M is not among the compiled modules (походження: --entry або маніфест file:line:col [target t] (entry))
Немає ні F/1, ні F/0<file>:<line>:<col>: entry function M:F/1 is not defined на оголошенні модуля
Явної F/A немає... entry function M:F/A is not defined; found F/N на визначенні з тим самим іменем, інакше на оголошенні модуля
Лише інші арності... is not defined; found F/N, but the entry takes the argument list (FUNCTION/1) or nothing (FUNCTION/0) на цьому визначенні
F/1 (інакше F/0) не експортовано<file>:<line>:<col>: entry function M:F/A is not exported на визначенні
Нічого не обрано, немає експорту main/1no entry point: no module exports main/1; choose the entry with --entry MODULE[:FUNCTION[/ARITY]] (an exported FUNCTION/1, or FUNCTION/0 run without the program arguments; FUNCTION defaults to main, ARITY to 1, else 0)
Нічого не обрано, кількаambiguous entry point: main/1 is exported by a, b; choose the entry with ... (та сама підказка)

Для цілей проєкту підказка також називає ключ маніфесту: ... --entry MODULE[:FUNCTION[/ARITY]] or with entry = "MODULE[:FUNCTION[/ARITY]]" in this target's [[targets]] table of the project manifest ..., наприклад:

[[targets]]
name = "app"
sources = ["src/*.erl"]
entry = "app:start"   # calls app:start/1 (or app:start/0); "app:start/0" calls app:start/0; plain "app" calls app:main/1

Аргументи

Entry(Argv) отримує правильний список рядків (списків кодових точок Unicode) без імені програми й початкових опцій runtime, в іншому незмінних і в тому самому порядку, як escript.

Опції runtime

Runtime читає свої опції зі змінної середовища CLAUSE_FLAGS (слова розділено пробілами й табуляціями, без лапок), а потім із початкових аргументів командного рядка, тож командний рядок має перевагу. Обидва джерела розбираються однаково; значення йде в наступному аргументі або після =.

ОпціяЕфект
--max-atoms NРозмір таблиці атомів, від 1 до 2^26 (67 108 864); типово 2^20 (1 048 576), як +t в OTP
--max-heap BYTESОбмеження на блок купи кожного процесу, фрагменти й binary поза купою, щонайменше мінімальна купа (233 слова); типово без обмеження
--max-stack BYTESОбмеження на стек фреймів кожного процесу; типово без обмеження
--min-heap-size WORDSТиповий min_heap_size кожного процесу, щонайменше 1, округлюється вгору до розміру купи, як +hms в OTP; не більше --max-heap; типово 233 (розміри)
--max-memory BYTESОбмеження на пам'ять усіх процесів разом (купи, binary поза купою, стеки); типово без обмеження
--schedulers NРобочі потоки планувальника, що виконують процеси, від 1 до 1 024; типово один на CPU з --scheduler-cpus, інакше один на логічний процесор, на якому дозволено виконувати програму (за її маскою спорідненості), як активні планувальники OTP (робочі потоки)
--scheduler-cpus SETПрив’язати робочий потік N до N-го CPU з SET, циклічно, як +sbt/+sct в OTP: performance (найшвидший клас ядер: P-ядра гібридного CPU, інакше всі CPU), all або список на кшталт 0-15 чи 2,4,6; кожен указаний CPU має входити до маски спорідненості. Типово без прив’язки (прив’язка до CPU)
--profile FILEЗаписати у FILE після завершення програми звіт про входи й власний час для кожної функції та кожного процесу (профілювання); типово вимкнено
--args-file FILEФайл опцій на кшталт vm.args в OTP: зарезервовано, повідомляє runtime option --args-file is not implemented
--Завершує опції runtime; усі наступні аргументи йдуть до Entry

У командному рядку розбір зупиняється на першому аргументі, що не є опцією runtime, тож prog data --max-atoms 9 передає програмі всі три аргументи. У CLAUSE_FLAGS кожне слово має бути опцією runtime. Недійсне значення, слово в змінній, що не є опцією, або --args-file зупиняють програму до реєстрації будь-якого модуля: clau: runtime failure: <reason>, код виходу 70. Кількість процесів не обмежена, а пам'ять типово не має обмеження. Значення в байтах десяткові без суфіксів і округлюються донизу до цілих слів; програма, що досягає обмеження, завершується збоєм resource_limit, код виходу 70 (обмеження пам'яті runtime).

Статус виходу

РезультатСтатус
Точка входу повертає значення (будь-яке)0
erlang:halt()0
erlang:halt(N), невід'ємне ціле числоN (хости POSIX зберігають молодші 8 бітів)
erlang:halt(Slogan) з рядкомГасло в stderr, потім 1 (без аварійного дампу)
erlang:halt(abort)Нативний abort (без скидання буферів)
Будь-який виняток, що виходить за межі точки входу, включно з throw і exit(normal)Звіт у stderr, 1
Процес точки входу завершено сигналом виходу (процеси)Повідомляється як неперехоплений exit, 1; причина normal: 0
Збій запуску runtime чи інфраструктури (невідповідність ABI, реєстрація, пам'ять до входу)Повідомлення в stderr, 70
Пам'ять хоста вичерпано (відмовлено в зростанні купи, binary поза купою чи стека; типово без обмеження пам'яті, вичерпання пам'яті)clau: runtime failure: entry call failed: out_of_memory, 70

Недійсні аргументи halt/1 породжують badarg у того, хто викликає. Коли точка входу завершується, програма виходить: інші процеси зупиняються без подальшого виконання, як із halt/1 в OTP після повернення з escript. Halt у будь-якому процесі завершує програму з його статусом, а збій runtime у будь-якому процесі завершує її з 70; виняток у запущеному процесі завершує лише цей процес (процеси).

halt/0,1, error/1,2,3, exit/1 і throw/1 можна викликати з префіксом erlang: або без нього; локальне визначення чи -compile({no_auto_import, ...}) залишає некваліфіковане ім'я локальним, як в OTP. halt/2 недоступна. halt(N) зберігає молодші 31 біт будь-якого невід'ємного цілого числа, як OTP. Гасло (slogan) — це правильний список щонайбільше з 1 023 кодових точок Unicode. Halt розмотує точку входу через перевірений канал помилок, як помилку, тож зупиняє програму лише після згенерованого прибирання.

Потоки виводу

Об'єкт запуску

Компіляція з явною точкою входу (--entry або entry маніфесту) додає модуль запуску після модулів пакета. З --emit він публікується як clausev1_start.{obj,o,ll,bc} поряд з артефактами модулів (ім'я не може збігтися з артефактом модуля). Він містить сталий abi::v1::StartupDescriptor (startup.hpp): ревізію ABI, ширину терма, кожен дескриптор модуля в порядку джерел, записи модуля/функції точки входу та прапорець escript. Його int main(int, char **) викликає CLAUSE_main_v1 з runtime, яка:

  1. Перевіряє дескриптор запуску й кожен дескриптор модуля на ревізію ABI та ширину до реєстрації будь-чого; невідповідність дає код виходу 70.
  2. Запускає runtime і реєструє всі модулі; будь-який збій зупиняє все до точки входу й відкидає runtime (код виходу 70), тож жоден код Erlang не виконується з неповним пакетом.
  3. Створює процес точки входу, будує argv і ставить виклик M:F/1 у чергу як головний процес, потім виконує його й кожен процес, який він запускає, на робочих потоках планувальника (процеси), доки той не завершиться.
  4. Відображає результат на статус виходу вище, друкуючи звіти після скидання stdout, потім звільняє кожен процес і завершує роботу runtime на кожному шляху (окрім halt(abort)).

clau -o компонує ці об'єкти сам (компонування). Ручне компонування (рецепт нативної обв'язки без вихідного файлу обв'язки):

& $tool --emit obj --entry app --artifact-dir build/app app.erl helper.erl
clang-cl /MT build/app/*.obj build/debug/lib/clause_runtime.lib /Fe:app.exe

Будь-яке Clang-сумісне компонування об'єктів із Clause::generated_program працює так само (див. tests/compiler/linking/startup.cmake).

Компонування

clau [-O0|-O2|-Os] -o PATH a.erl b.erl ... (або --project FILE [--target T] -o PATH для однієї обраної цілі) компілює пакет у пам'яті, додає об'єкт запуску для точки входу і компонує виконуваний файл:

clau -O2 -o build/demo examples/compile/answer.erl examples/compile/client.erl
./build/demo          # build/demo.exe on Windows

-Os додатково розміщує кожну згенеровану функцію й функцію runtime та кожен об'єкт даних в окремій секції й компонує з --gc-sections (ELF), -dead_strip (Mach-O) або /OPT:REF /OPT:ICF (MSVC), тож код, якого не досягає жоден шлях від точки входу, видаляється.

--lto компілює модулі й модуль запуску в біткод LLVM замість об'єктних файлів і компонує з -flto -fuse-ld=lld, тож LLD оптимізує згенерований код програми як одне ціле разом з архівом нативного runtime. Дескриптори модулів і запуску, експортовані точки входу й main лишаються вживаними й зберігаються; програми поводяться так само, як без цієї опції. Біткод цього компілятора читає лише LLD, тож --lto приймається для цілей Windows MSVC і ELF; інші завершуються збоєм до компонування з --lto is not supported for target <triple>: it needs LLD. --lto потребує виводу виконуваного файлу (-o або збирання проєкту з компонуванням). Встановлені Clang/LLD мають бути того самого релізу LLVM, що й clau (23.1).

Живучість експортів

Виконуваний файл пропускає експорти, яких не може досягти, тож dead-stripping у -Os і --lto видаляють бібліотечний код, який програма ніколи не викликає. Одразу після розв'язання викликів, до виведення типів, аналіз усього пакета починається з функції входу й позначає живим усе, що жива функція викликає напряму (також через -import і автоімпорти OTP), кожен fun F/A і літеральний fun M:F/A, а також літеральні цілі apply/3, spawn/3, spawn_link/3, spawn_monitor/3, spawn_opt/4,5, spawn_request/3,5, hibernate/3 і make_fun/3 (кожну арність M:F, коли список аргументів не літеральний), і file:write/2, щойно живими стають io:format/3, io:fwrite/3, io:put_chars/2 або io:nl/1 (їхні вбудовані функції пишуть через нього на пристрій-процес). Аналіз лишається консервативним:

Функція, якої не досягає нічого живого, взагалі не отримує коду. Пропущений експорт зберігає своє ім'я в таблиці експортів модуля без коду, тож function_exported/3 і module_info(exports) відповідають так, ніби її експортовано, тоді як динамічний виклик, що до неї доходить, породжує undef (відмінності). Таке можливе, лише коли ім'я модуля надходить ззовні аналізу: з трасування стека, терма винятку чи даних ззовні програми. --verbose трасує результат для кожного модуля (phase=liveness detail="dropped-exports=3" або detail="kept-all-exports reason='m' is used as a value in main:main/1").

Функція, якої не досягає нічого живого, також ніколи не проходить виведення типів і перевірку щодо свого -spec: виведення охоплює лише живий код, що швидше для програм, які використовують малу частину великих бібліотечних модулів, і може лише звузити факти живих функцій, бо недосяжні місця виклику більше не об'єднують свої аргументи. Тому специфікація, якій суперечить лише недосяжний код, повідомляється для артефактів --emit, але не для виконуваного файлу.

--keep-exports (або опція проєкту keep_exports = true) зберігає кожен експорт доступним для виклику. Аналіз застосовується до виконуваних файлів (-o або збирання проєкту з компонуванням) і до інспекцій IR та асемблера із заданою точкою входу (--entry або entry з маніфесту цілі), які тоді показують код виконуваного файлу; артефакти --emit, призначені для інших програм, зберігають усі експорти.

-g зберігає таблиці рядків Erlang для налагоджувачів: у виконуваному файлі (ELF) або в <name>.pdb, опублікованому поруч із ним (цілі MSVC) (налагодження).

Збій (код виходу 1)Діагностика
Немає Clangcannot find clang++ or clang on PATH; install LLVM/Clang or pass --linker / linker not found: X
Немає runtimeruntime library not found: P; build the clause_runtime target or pass --runtime-library
Не архівruntime library is not a static library: P: ...
Неправильна цільruntime library P contains x86_64 coff objects, but the executable targets T; ...
Помилка компонуванняlinking O failed: <clang> exited with status N:, а за ним вивід компонувальника (перші 64 КіБ)
Неправильне призначенняoutput directory does not exist: D, artifact destination is not a regular file: O, artifact destination aliases an input: O

Escript

Вихідний файл, перший рядок якого починається з #!, компілюється як escript у будь-якому режимі й з будь-яким ім'ям файлу (позиційні вхідні файли чи файли .erl проєкту). Правила відповідають escript з OTP 29 для скриптів у вихідних кодах:

Порівняння з OTP

Еталони для фікстур програм виконують точку входу під OTP за тими самими правилами (оракул); той самий оракул генерує еталонні випадки виконуваних файлів, які наскрізний запускач компонує й виконує за кожної політики. Відмінності від escript для звичайних модулів: неперехоплені винятки дають код виходу 1 замість 127, і main/1 має бути експортована. Вихідні коди escript зберігають правила OTP (див. вище).