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

Англійський оригінал змінився після перекладу, тож частина тексту може бути застарілою. Читати англійською

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

Компіляція

clau компілює модулі Erlang/OTP 29 через LLVM у перевірене IR, біткод або нативні об'єктні файли. -o/--output компонує позиційні вхідні файли (або одну вибрану ціль проєкту), їхній стартовий об'єкт точки входу і runtime у виконуваний файл (компонування); збирання проєктів компонують виконувані цілі в їхні виводи з маніфесту (проєкти). Згенеровані об'єктні файли також можна запускати через обв'язку C++, скомпоновану з runtime (див. приклад нижче).

Прийнята підмножина вихідного коду

Іменовані модулі звичайної мови OTP 29: впорядковані клаузи функцій зі зразками і guard; зіставлення, конструктори, оператори та вбудовані функції (вбудовані функції); case, if, receive, catch, try ... of ... catch Class:Reason:Stack ... after з трасуваннями стека, maybe ... else ... end; comprehension для списків, binary і map (зразки); кортежні та native records; fun, локальні, віддалені й динамічні виклики (M:F(...), apply/2,3); процеси і порти. Виклики виконуються на явних фреймах процесу з належними хвостовими викликами (модель виконання); рекурсію в тілі обмежує стек процесу (за замовчуванням необмежений), а не нативний стек. Модулі, які пакет називає, але не оголошує, беруться з бібліотеки. Кваліфікатори зіставлення в comprehension експериментальної можливості compr_assign відхиляються як [heap expressions] notimpl.

Прийняті атрибути: module, export, file, кортежний і нативний record, export_record, import_record, форми type/spec, doc/moduledoc, behaviour/behavior (поведінки), будь-який інформаційний атрибут (vsn, author, copyright, deprecated, dialyzer, визначений користувачем -name(Term); зберігаються для module_info(attributes)), -import(Module, [F/A, ...]) (імпорти) і -compile з параметрами, що не змінюють нічого з того, що генерує Clause: {no_auto_import, ...}, параметри попереджень (nowarn_*, warn_*, {nowarn_unused_function, [...]}) і підказки щодо оптимізації, налагодження та звітування (inline, {inline, [F/A]}, {inline_size, N}, {inline_effort, N}, inline_list_funcs, debug_info, deterministic, report*, verbose, ...) і власний параметр Clause {clause_liveness, strict} (сувора досяжність). {parse_transform, Module} запускає перетворення на Erlang/OTP хоста перед аналізом (parse transforms). on_load, nifs, export_all, інші параметри compile і параметризовані модулі відхиляються; діагностика називає їх, наприклад [behavior-changing attributes] notimpl [module="m" operation="-compile option export_all"]. Кожен модуль отримує module_info/0,1. Вихідні файли, що починаються з #!, дотримуються правил escript (неявний модуль і експорт main/1, -mode приймається). Форми type/spec аналізуються, але ніколи не змінюють згенерований код. Режими лише для синтаксису (--parse-check, --print-ast, --print-source, ...) приймають повну граматику.

Сувора досяжність

-compile({clause_liveness, strict}). повідомляє аналізу досяжності експортів, що модуль ніколи не називає викликаний код через власні атоми. Його атомні літерали не утримують експорти інших модулів (власне ім'я все ще утримує власні, оскільки кортежі помилок називають його для format_error/1), теги record не утримують жодних, а вбудовані функції, що створюють атоми, і binary_to_term/1,2 не вважаються ані джерелом імен модулів, ані динамічним викликом. Також він не може передавати імена інших модулів у вбудовані функції, що викликають їх за іменем, як-от spawn/4 з вузлом або erlang:get_module_info/1,2. Його власні виклики за іменем усе ще враховуються: M:F(...) або apply/3 з літеральним модулем і функцією, визначеною під час виконання, утримують експорти цього модуля, а виклики з модулем, визначеним під час виконання, є динамічними викликами й досягають модулів, які називають атоми інших модулів.

Параметр не змінює згенерованого коду, лише набір експортів, які зберігає виконуваний файл. Хибна обіцянка вилучає експорт, який модуль викликає за іменем, що потім спричиняє undef. Бібліотечні модулі задають його там, де їхні атоми — лише дані: epp (імена токенів і макросів, машини, застосунків), erl_error (атом-заповнювач у надрукованих списках аргументів; error_info називає модулі коду, що спричинив помилку), erl_features (список можливостей beam-файлу) і file (причини помилок, теги запитів; модулі, названі в запиті io, є атомами відправника запиту). Без нього apply/3 у file:io_request/3 і list_to_atom/1 у epp утримували кожен експорт програми, що препроцесує файл, включно з усім erl_eval; з ним (і обчисленням умов -if у C++) додатковий час її компіляції порівняно з hello-world скорочується приблизно на 40%.

Запуск прикладу зі скомпільованими модулями

client:main/1 робить приклад програмою:

./build/debug/bin/clau -O2 -o build/demo examples/compile/answer.erl examples/compile/client.erl
./build/demo     # prints 42, -7 and {record,map,binary,list,integer,other}

Ті самі модулі можна запустити й через обв'язку C++. З Developer PowerShell для Windows x64 і зібраним компілятором:

$tool = './build/debug/bin/clau.exe'
& $tool -O0 --emit obj --artifact-dir build/example-aot examples/compile/answer.erl examples/compile/client.erl
cmake -S examples/compile -B build/example-native -G Ninja -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_BUILD_TYPE=Debug "-DGENERATED_DIR:PATH=$((Resolve-Path build/example-aot).Path)"
cmake --build build/example-native
./build/example-native/bin/Debug/compiled_modules.exe

Вона друкує 42, -7, record, map, binary, list, integer, other, по одному на рядок. Обв'язка явно реєструє модулі, створює контекст і декодує результати; це приклад хоста, а не робоча точка входу. В Unix використовуйте build/debug/bin/clau, clang++ і -DGENERATED_DIR="$PWD/build/example-aot". CTest codegen_examples виконує ці команди за кожної політики.

Інші дії над тими самими вихідними файлами:

& $tool -O2 --emit llvm-ir --artifact-dir build/example-ir examples/compile/answer.erl examples/compile/client.erl
& $tool --print-types --verbose examples/compile/answer.erl examples/compile/client.erl
& $tool --print-ir --print-optimized-ir examples/compile/answer.erl examples/compile/client.erl
& $tool -O2 --no-type-specialization --verbose examples/compile/answer.erl examples/compile/client.erl

Друк abstract format

--print-abstr (дія фронтенду) друкує кожен розібраний модуль як форми OTP abstract format, які epp передав би parse transform, по одному терму file:consult/1 на рядок — ті самі терми, що записує erlc +to_abstr (перетворення).

Друк вихідного коду

--print-source (дія фронтенду, як --print-ast) друкує кожен розібраний модуль як вихідний код Erlang; --print-types друкує той самий текст з анотаціями типів. Принтер (print_source, expression_source, type_source у printing.hpp; compiler/src/printing/source_*) придатний для повторного використання:

Параметри

ПараметрПоведінка
--emit obj|llvm-ir|llvm-bcПублікує один артефакт на модуль
--artifact-dir DIRКорінь артефактів (потребує --emit)
-o PATH / --output PATHКомпонує виконуваний файл (компонування); конфліктує з --emit
--linker PATH, --runtime-library PATHДрайвер Clang і архів runtime для -o
--no-cacheНе читати й не записувати кеші збирання
--cache-inference-in-dirs PATTERNМожна повторювати: модулі у відповідних каталогах зберігають результати виведення типів у кеші збирання, на додачу до неявних каталогів застосунків OTP (кеш виведення)
--build-dir DIRДе Clause тримає тимчасові файли (у DIR/tmp); замінює build_dir кожної вибраної цілі проєкту; позиційні вхідні файли без нього його не мають (каталог збирання)
--entry MODULE[:FUNCTION[/ARITY]]Функція точки входу виконуваного файлу з арністю 1 (інакше з арністю 0; /0 або /1 вибирає одну); перевіряється в кожному режимі компіляції й додає стартовий артефакт clausev1_start (виконувані файли); інспекції IR і асемблера тоді пропускають те, чого вона не може досягти (живучість експортів)
--target-triple TRIPLEЦільова машина; --target — це вибір цілі проєкту
-O0 / -O2 / -OsЗагальний код за замовчуванням + LLVM O0 / обмежена спеціалізація + LLVM O2 / LLVM Os, без спеціалізації, одна секція на символ і вилучення компонувальником коду та даних, на які немає посилань
--no-type-specializationВимикає варіанти незалежно від порядку параметрів
--jobs <n>Оптимізувати й генерувати об'єктні модулі в n потоках (за замовчуванням: усі апаратні потоки), кожен модуль у власному контексті LLVM; об'єктні файли однакові за будь-якої кількості; --jobs 1, інспекції IR та асемблера зберігають усі модулі в одному контексті
--ltoКомпонує модулі як біткод з оптимізацією під час компонування LLD (цілі Windows MSVC і ELF; потребує -o або збирання проєкту з компонуванням) (компонування)
--keep-exportsЗберігає кожен експорт виконуваного файлу доступним для виклику, замість пропускати ті, яких він не може досягти; потребує -o, збирання проєкту з компонуванням або інспекції IR/асемблера (живучість експортів)
--shellСкомпонувати інтерактивну оболонку Erlang і всі бібліотечні модулі, зберігши можливість викликати кожен експорт; без --entry виконуваний файл запускає оболонку, тож clau --shell -o erl без вхідних файлів збирає оболонку лише над бібліотекою; потребує -o або збирання проєкту з компонуванням (інтерактивна оболонка)
--fips=PATHЗафіксувати програму в режимі FIPS з наданим користувачем провайдером OpenSSL FIPS у PATH (модуль і fipsmodule.cnf, що копіюються поруч із виконуваним файлом); передбачає --with-crypto; відсутній або некоректний провайдер зупиняє збирання (FIPS)
--with-crypto[=PATH]Скомпонувати архів crypto runtime і додати бібліотечний модуль crypto до пакета для програм, що досягають crypto лише через динамічні виклики (програми, які викликають його, і так компонуються з ним); PATH — встановлений OpenSSL ≥ 3.1.2 або дерево вихідного коду для компонування замість того, з яким зібрано Clause; потребує -o або збирання проєкту з компонуванням (crypto)
-gТаблиці рядків Erlang в об'єктних файлах і скомпонованих виконуваних файлах (CodeView/PDB для цілей MSVC, DWARF для решти) (налагодження)
--print-ir / --print-optimized-irПеревірене IR до/після проходів LLVM, з рядками вихідного коду Erlang як коментарями
--print-asmАсемблер цілі для кожного модуля після вибраного конвеєра, де кожна послідовність машинного коду має коментар з інструкцією IR і рядком Erlang, з яких вона походить (лістинги асемблера)
--print-typesКожен модуль як вихідний код Erlang з анотаціями виведених типів (семантика); зупиняється перед LLVM
--verboseПодії фаз [pp], [parse] і [comp] у stderr, потім зведення індексу каталогів [index] ...
--print-stage-times[time] <stage> <subject>: <ms> ms у stderr: plan для кожного маніфесту проєкту (його завантаження й розгортання шаблонів кожної вибраної цілі), preprocess, parse і transform (модулі з parse transforms) для кожного вихідного файлу, compile для кожного пакета (кількість модулів, ціль проєкту), link для кожного виконуваного файлу; (cached) іде після етапу, що повторно використав кешований результат. Перед підсумком compile пакета йдуть його фази з тим самим суб'єктом: index, calls, liveness (виконувані файли та інспекції з точкою входу), declared-types, inference-cache (з N of M covered modules reused, кеш виведення), inference, inference-store, contracts, lower, optimize, emit (ті, що виконувалися; за виведення об'єктних файлів у кількох потоках --jobs, optimize також переміщує кожен модуль у власний контекст LLVM і перевіряє його, а emit перевіряє й генерує модулі паралельно). Після inference: рядки inference pass N з кроками роботи проходу з бюджету роботи виведення (N of BUDGET work steps) і тим, як він завершився (inputs changed, fun results changed, settled або останній прохід після неусталених проходів), із назвою функції, що виконувалася, коли бюджет вичерпався, потім (кожен прохід після першого) ; skipped: N components, W work steps: компоненти, збережені з останнього обходу, бо нічого з прочитаного ними не змінилося, і вартість тих обходів (врахована в робочих кроках проходу), потім ; N lattice joins — виконані проходом об'єднання ґратки (вкладені теж враховуються), з , costly: C components given term() inputs, коли дорогі рекурсивні компоненти втратили локальні входи (виведення типів), а далі (кожен прохід, що збирав локальні входи) ; inputs: F functions, S slots changed (kinds) з видом зміни кожного зміненого вхідного слота (first за відсутності попереднього, integer bound, union member на верхньому рівні, shape, deeper для вкладеності контейнерів, function types, term()) і N fun reads not final (значення fun F/A, що прочитали зведення, яке згодом змінилося), потім ; graph: N nodes (+M), W widenings від попереднього проходу і ; clean: C of K components (A inputs differ, B callees differ), X changed, T ms (P% of pass), S work steps: компоненти, чиї входи членів і зведення безпосередньо викликаних функцій дорівнюють попередньому проходу, ті з них, чиї зведення все одно змінилися, і витрачені на них час та робота; потім inference lattice (без часу) з кількістю виконань кожної операції ґратки за всі проходи (join двох членів, вкладені теж враховуються, widen, meet, within, subtract, truncate) і кількістю викликів, що повторили операцію з тими самими операндами раніше у своєму проході й у будь-якому проході (їх обслуговують відповіді, збережені графом, а вкладені операції відповіді не виконуються й не враховуються повторно), потім об'єднання іншої кількості членів і пошуки вкладеності контейнерів із тими, для яких граф зберіг відповідь (відстеження повторів уповільнює профільоване виведення приблизно на 7%); потім inference churn з часом збирання входів і кількістю локальних вхідних слотів, що змінилися один раз, двічі, ... за всі проходи, і inference churned <module>:<function>/<arity> #<argument> (без часу) для 10 найчастіше змінюваних слотів із видом останньої зміни та фактами до й після неї (кожен обрізано до 200 символів); потім (без часу) до 10 компонентів inference clean-changed <module>:<function>/<arity>, які були незмінними за входами й викликами, але змінилися, з проходом першого виявлення й частотою (залежність, відмінна від входів і прямих викликів, як-от fun F/A); потім (без часу) inference facts N після кожного проходу, що збирав входи: факти кожного зведення й локального входу, найбільший і середній розмір їхнього дерева (вузли враховуються один раз на шлях), найширше об'єднання, кількість тих, що досягають бюджетів глибини контейнерів, членів об'єднання й елементів кортежу/map, зведення з найбільшою кількістю (32) типів функцій і залежні факти; потім inference largest з найбільшим фактом кожного з 3 найдовших за часом компонентів (обрізано до 200 символів); потім inference slowest <module>:<function>/<arity> для 10 функцій (рекурсивні групи називають одного члена and N more), що забрали найбільше часу за всі проходи, з їхніми кроками роботи й часткою цієї роботи в бюджеті проходів, які їх виконували (877450 work steps, 43.9% of 10 passes of 200000 budget), widened to term(), коли бюджет розширив один із їхніх фактів, а також об'єднаннями ґратки, виконаними їхніми обходами, і створеними вузлами графа; потім inference costliest <module>:<function>/<arity> для 10 функцій тривалістю щонайменше 10 мс із найбільшим часом на робочий крок, з цим часом у мікросекундах, робочими кроками, об'єднаннями ґратки (і об'єднаннями на робочий крок) та створеними вузлами; потім inference rounds <module>:<function>/<arity> and N more для 10 найповільніших рекурсивних груп: розв'язання за всі проходи, раунди щодо обмеження на одне розв'язання (8 + 4 на члена), розв'язання, що досягли обмеження або витратили свою восьму частину бюджету проходу (кожен член розширено до term()), розв'язання, що одразу здалися, бо так зробив попередній прохід (remembered), або завершилися з вичерпаним бюджетом проходу, обходи членів (і пропущені, бо нічого з прочитаного ними не змінилося, і відтворені: пізніше розв'язання повторило останній обхід члена, бо нічого з прочитаного ним не змінилося) і частка обходів та відтворень, що змінили зведення (зросло, змінилися входи типів функцій або повернулося до одного з останніх 4 зведень члена), та змінені члени на раунд останнього розв'язання в десяти групах раундів, потім ; N solves repeated the previous fixed point, M reached it after R rounds in total: розв'язання, що завершилися на зведеннях, якими завершився попередній прохід, і розв'язання, що досягли їх до збіжності, з кількістю витрачених раундів (які можна було б пропустити, почавши з них). Кешована компіляція фаз не друкує
--asn1-option OPTION, --asn1-outdir DIR, --generate-asn1Компілювати входи .asn/.asn1/.set.asn у Module.erl і Module.hrl з параметром asn1ct (можна повторювати), у DIR (додається до шляху include) або лише згенерувати їх (ASN.1)
--print-envДрукує розв'язане середовище компіляції як TOML у stdout і завершується, не обробляючи вихідних файлів (звіт про середовище)
--print-inputsПрепроцесує й розбирає вхідні файли, додає модулі, які вони називають, як це робить компіляція, потім перелічує кожен вихідний файл пакета в stdout і зупиняється перед компіляцією (проєкти)

Лістинги асемблера

--print-asm запускає вибраний конвеєр (як --print-optimized-ir), а потім друкує асемблер кожного вхідного модуля з тієї самої цільової машини, що записує об'єктні файли, по одному модулю. Коментарі використовують маркер цілі (# на x86, // на AArch64 ELF, ; на Apple, @ на ARM):

# erlang answer.erl:3: value() -> 42.
#     ir store i64 687, ptr %frame.slots, align 8
	movq	$687, 32(%r15,%r12,8)
#     ir br i1 %4, label %return.inline, label %return.service

Звіт про середовище

--print-env перевіряє весь командний рядок (конфлікти, читабельність позиційних вхідних файлів, планування проєкту), потім друкує те, що використала б ця команда, як один документ TOML у stdout, завершується з кодом 0 і не читає жодного вихідного файлу. Шляхи абсолютні, з /.

# clau compile environment (--print-env)
command = ["clau", "--print-env", "-O2", "-o", "app", "main.erl"]  # as given
actions = ["link"]          # see below
working_directory = "/work"

[[targets]]                 # one per selected project target; one for positional inputs
name = "app"                # project targets only
project = "/work/project.toml"
sources = ["/work/src/main.erl"]           # globs and search paths resolved
library_directory = "/clau/library"     # application dirs, searched before source_search_paths
build_dir = "/work/build"   # "" for positional inputs without --build-dir
entry = "main:main"         # or "auto" when linking without --entry
output = "/work/app"        # linking only: final name (".exe" on Windows)
linker = "/usr/bin/clang++" # linking only; "" when none is found
runtime_library = "/clau/lib/libclause_runtime.a"

[targets.options]           # manifest option names
source_search_paths = []    # directories, patterns expanded
include_dirs = []           # search order, patterns expanded
defines = []
applications = { "stdlib" = "/otp/lib/stdlib" }  # last mapping wins
enabled_features = ["maybe_expr"]                # OTP defaults plus changes

[targets.backend]
target_triple = "x86_64-unknown-linux-gnu"  # host when not given
optimization = "O2"
type_specialization = true  # O2 without --no-type-specialization
debug_info = false
lto = false
keep_exports = false        # --keep-exports or the project option
shell = false               # --shell or the project option
emit = "none"               # or obj, llvm-ir, llvm-bc
artifact_dir = "/work/build/aot"

(Масиви друкуються по одному елементу на рядок.) actions перелічує дії фронтенду (print-pp, parse-check, print-ast, print-source, print-abstr, інакше preprocess-check; print-inputs сам замінює їх), інакше інспекції (print-types, print-ir, print-optimized-ir, print-asm), інакше emit, link (-o) або compile. У збиранні проєкту цілі з output також компонуються. Модулі, знайдені пізніше за іменем (через source_search_paths або бібліотеку), не перелічуються: щоб їх знайти, потрібен розбір (--print-inputs робить це). --print-env не можна поєднувати з --new-project.

Кеш компіляції

Пакет із каталогом збирання (кожна ціль проєкту, позиційні вхідні файли з --build-dir) зберігає результат компіляції в кеші збирання: діагностику в порядку появи, збій і, коли їх потребує виконуваний файл чи --emit, об'єктні файли (або біткод) кожного модуля й модуля запуску. Пізніше збирання того самого пакета з тими самими параметрами відтворює діагностику й компонує або публікує збережені об'єктні файли точно так само, як свіжі; --print-stage-times тоді показує [time] compile ... (cached). --print-ir, --print-optimized-ir, --print-asm і --print-types завжди обчислюють наново, а --no-cache не читає й не записує.

Бекенд

SDK LLVM

Стабільний LLVM 23.1.x, щонайменше 23.1.1. LLVM — залежність хоста: архітектура, стандартна бібліотека C++ і CRT Windows мають відповідати інструменту компілятора. Код проєкту зберігає винятки/RTTI; жоден виняток не може розкручуватися через LLVM. Runtime ніколи не використовує LLVM.

Еталонне налаштування Windows x64: clang-cl 23.1.2 хоста в C:/Program Files/LLVM/bin, SDK thirdparty/clang+llvm-23.1.2-x86_64-pc-windows-msvc, інструменти Visual Studio 18 x64, Windows SDK 10.0.26100.0, Ninja, /MT, _ITERATOR_DEBUG_LEVEL=0. Автоматичний вибір застосовує налаштування /MT та ітераторів; явний LLVM_DIR — ні, і тоді пробне компонування завершується невдачею.

PCRE2

Вбудовані функції re у runtime використовують PCRE2 10.47 — реліз, який постачає OTP 29 (erts/emulator/pcre/vendor.info), тож синтаксис шаблонів, параметри й захоплення поводяться як в OTP: 8-бітні кодові одиниці, підтримка Unicode, без JIT, стандартний pcre2_match (патч OTP для поступання не перенесено). PCRE2 має ліцензію BSD-3-Clause з винятком PCRE2 для скомпільованих двійкових файлів; жоден файл PCRE2 не закомічено. CMake (cmake/Pcre2Dependencies.cmake) розв'язує його в такому порядку:

  1. -DCLAUSE_PCRE2_ROOT=<source directory> має перевагу (без завантаження); версія в його configure.ac має бути 10.47.
  2. Встановлений PCRE2 версії щонайменше 10.47 (CLAUSE_PCRE2_SYSTEM=ON, типово): пакет CMake (find_package(pcre2 CONFIG COMPONENTS 8BIT), перевага статичним бібліотекам), інакше pkg-config libpcre2-8, де статична libpcre2-8.a має перевагу над спільною бібліотекою.
  3. Зафіксований реліз, завантажений у thirdparty/pcre2-10.47 і перевірений (pcre2-10.47.tar.bz2, SHA-256 47fe8c99461250d42f89e6e8fdaeba9da057855d06eb7fc08d9ca03fd08d7bc7); -DCLAUSE_DOWNLOAD_PCRE2=OFF вимикає завантаження.

Каталог вихідного коду (1 або 3) збирає об'єктну бібліотеку clause_pcre2 з перелічених файлів .c з поширюваними config.h.generic, pcre2.h.generic і pcre2_chartables.c.dist, скопійованими в дерево збирання (без кроку генерації, без прапорців попереджень проєкту, поза clang-tidy і Lizard), а її об'єктні файли додаються до самого clause_runtime: архів самодостатній, і кожен споживач (компонування clau, вкладені тестові програми, runtime_link, examples/compile) отримує PCRE2 разом із ним. Встановлена бібліотека натомість стає додатковим входом компонувальника, записаним у clause_runtime.link поруч з архівом (один вхід на рядок), який clau додає до кожного компонування; спільна бібліотека робить виконувані файли залежними від неї під час виконання. За платформами: Windows використовує завантаження (системний PCRE2 на практиці не шукається); Linux і macOS використовують встановлений PCRE2 10.47 або новіший, якщо він є, інакше завантаження. PCRE2 завжди компонується в runtime, а також у програми, які ніколи не викликають re.

zlib

Модуль zlib у runtime і стиснуті зовнішні терми використовують zlib 1.3.2 — реліз, який постачає OTP 29 (erts/emulator/zlib/vendor.info), тож стиснуті байти дорівнюють байтам OTP. CMake компілює вихідні файли його бібліотеки (cmake/ZlibDependencies.cmake, clause_add_runtime_zlib) в об'єктну бібліотеку clause_runtime_zlib, чиї об'єктні файли приєднуються до clause_runtime, як і в PCRE2; -DCLAUSE_RUNTIME_ZLIB_ROOT=<source directory> має перевагу, інакше реліз завантажується в thirdparty/zlib-1.3.2 і перевіряється (SHA-256 bb329a0a2cd0274d05519d61c667c062e06990d72e125ee2dfa8de64f0119d16) на кожному хості. Ліцензія zlib — ліцензія zlib. LLVM компілятора має власний zlib; вони ніколи не зустрічаються в одному двійковому файлі.

zstd

Модуль zstd у runtime використовує zstd 1.5.7 — реліз, який постачає OTP 29 (erts/emulator/zstd/vendor.info), тож кадри дорівнюють кадрам OTP. CMake компілює вихідні файли його бібліотеки (common, compress, decompress; однопотоково, без асемблерного декодера; cmake/ZstdDependencies.cmake, clause_add_runtime_zstd) в об'єктну бібліотеку clause_runtime_zstd, чиї об'єктні файли приєднуються до clause_runtime, як і в zlib; -DCLAUSE_RUNTIME_ZSTD_ROOT=<source directory> має перевагу, інакше реліз завантажується в thirdparty/zstd-1.5.7 і перевіряється (SHA-256 eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3); це той самий архів, який використовує збирання LLVM для Windows. Ліцензія zstd — BSD-3-Clause (або GPL-2.0).

OpenSSL

Вбудовані функції crypto у runtime використовують libcrypto від OpenSSL, зафіксовано 4.1.0, приймається будь-який реліз від 3.1.2 (останнього з провайдером, що пройшов валідацію FIPS), в окремому архіві clause_runtime_crypto, з яким компонуються лише програми, що викликають crypto. Джерело бібліотеки, її збирання з вихідного коду в Clause і clau, компонування (--with-crypto[=PATH]) і режим FIPS (--fips=PATH) описано в crypto.

Інші передумови: CMake ≥ 3.28, компілятор C++23, Boost ≥ 1.90, toml++ 3.4.0 та інструменти перевірки якості. OTP потрібен лише для аудитів за бажанням і регенерації фікстур.