Clause
← Toute la documentation

L'original anglais a changé depuis cette traduction ; certaines parties peuvent donc être obsolètes. Lire en anglais

Traduit de l'original anglais · 1e3a39c8-dirty · 2026-10-26

Compilation

clau compile des modules Erlang/OTP 29 via LLVM en IR vérifié, en bitcode ou en objets natifs. -o/--output lie les entrées positionnelles (ou une cible de projet sélectionnée), leur objet de démarrage d'entrée et le runtime en un exécutable (édition de liens) ; les builds de projet lient les cibles exécutables vers les sorties de leur manifeste (projets). Les objets émis peuvent aussi s'exécuter via un harnais C++ lié au runtime (voir l'exemple ci-dessous).

Sous-ensemble de source accepté

Modules nommés du langage OTP 29 ordinaire : clauses de fonction ordonnées avec motifs et guards ; correspondances, constructeurs, opérateurs et builtins (builtins) ; case, if, receive, catch, try ... of ... catch Class:Reason:Stack ... after avec traces de pile, maybe ... else ... end ; comprehensions de liste, de binary et de map (motifs) ; records tuples et native records ; funs, appels locaux, distants et dynamiques (M:F(...), apply/2,3) ; processus et ports. Les appels s'exécutent sur des cadres de processus explicites avec de vrais appels terminaux (modèle d'exécution) ; la récursion non terminale est bornée par la pile du processus (sans plafond par défaut), non par la pile native. Les modules qu'un lot nomme sans les déclarer proviennent de la bibliothèque. Les qualificateurs de correspondance dans les comprehensions de la fonctionnalité expérimentale compr_assign sont rejetés avec [heap expressions] notimpl.

Attributs acceptés : module, export, file, record tuple et natif, export_record, import_record, formes type/spec, doc/moduledoc, behaviour/behavior (behaviours), tout attribut informatif (vsn, author, copyright, deprecated, dialyzer, -name(Term) défini par l'utilisateur ; conservés pour module_info(attributes)), -import(Module, [F/A, ...]) (imports) et -compile avec des options qui ne changent rien à ce qu'émet Clause : {no_auto_import, ...}, les options d'avertissement (nowarn_*, warn_*, {nowarn_unused_function, [...]}) et les indications d'optimisation, de débogage et de rapport (inline, {inline, [F/A]}, {inline_size, N}, {inline_effort, N}, inline_list_funcs, debug_info, deterministic, report*, verbose, ...) et l'option propre à Clause {clause_liveness, strict} (vivacité stricte). {parse_transform, Module} exécute la transformation sur l'Erlang/OTP de l'hôte avant l'analyse (parse transforms). on_load, nifs, export_all, les autres options compile et les modules paramétrés sont rejetés ; le diagnostic les nomme, par exemple [behavior-changing attributes] notimpl [module="m" operation="-compile option export_all"]. Chaque module reçoit module_info/0,1. Les sources commençant par #! suivent les règles des escripts (module implicite et export de main/1, -mode accepté). Les formes type/spec sont analysées mais ne modifient jamais le code généré. Les modes purement syntaxiques (--parse-check, --print-ast, --print-source, ...) acceptent la grammaire complète.

Vivacité stricte

-compile({clause_liveness, strict}). indique à la vivacité des exports que le module ne nomme jamais le code qu'il appelle par ses propres atomes. Ses littéraux atomes ne conservent les exports d'aucun autre module (son propre nom conserve toujours les siens, car les tuples d'erreur le nomment pour format_error/1), ses étiquettes de records n'en conservent aucun, et ses builtins produisant des atomes et binary_to_term/1,2 ne comptent ni comme source de noms de modules ni comme appel dynamique. Il ne peut pas non plus passer les noms d'autres modules aux builtins qui les appellent par leur nom, comme spawn/4 avec un nœud ou erlang:get_module_info/1,2. Ses propres appels par nom comptent toujours : M:F(...) ou apply/3 avec un module littéral et une fonction connue au runtime conservent les exports de ce module, et les appels avec un module connu au runtime sont des appels dynamiques, atteignant les modules nommés par les atomes des autres modules.

L'option ne change aucun code généré, seulement les exports qu'un exécutable conserve. Une promesse incorrecte omet un export que le module appelle par son nom, ce qui lève alors undef. Les modules de bibliothèque l'activent lorsque leurs atomes ne sont que des données : epp (noms de jetons et de macros, la machine, noms d'applications), erl_error (un atome de substitution dans les listes d'arguments affichées ; error_info nomme les modules du code qui lève l'erreur), erl_features (la liste des fonctionnalités d'un fichier beam) et file (raisons d'erreur, étiquettes de requêtes ; les modules nommés par une requête d'E/S sont les atomes du demandeur). Sans elle, l'apply/3 de file:io_request/3 et le list_to_atom/1 d'epp conservaient chaque export d'un programme qui prétraite un fichier, tout erl_eval compris ; avec elle (et les conditions -if évaluées en C++), son surcoût de compilation par rapport à un hello-world baisse d'environ 40 %.

Exécuter l'exemple de module compilé

client:main/1 fait de l'exemple un programme :

./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}

Les mêmes modules s'exécutent aussi via un harnais C++. Depuis un Developer PowerShell Windows x64 avec le compilateur construit :

$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

Il affiche 42, -7, record, map, binary, list, integer, other, un par ligne. Le harnais enregistre explicitement les modules, crée un contexte et décode les résultats ; c'est un hôte d'exemple, pas un point d'entrée de production. Sous Unix, utiliser build/debug/bin/clau, clang++ et -DGENERATED_DIR="$PWD/build/example-aot". Le test CTest codegen_examples exécute ces commandes sous chaque politique.

Autres actions sur les mêmes sources :

& $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

Affichage du format abstrait

--print-abstr (une action du frontend) affiche chaque module analysé sous forme des formes du format abstrait d'OTP qu'epp remettrait à un parse transform, un terme file:consult/1 par ligne, les mêmes termes qu'écrit erlc +to_abstr (transforms).

Affichage du source

--print-source (une action du frontend, comme --print-ast) affiche chaque module analysé sous forme de source Erlang ; --print-types affiche le même texte avec des annotations de type. L'afficheur (print_source, expression_source, type_source dans printing.hpp ; compiler/src/printing/source_*) est réutilisable :

Options

OptionComportement
--emit obj|llvm-ir|llvm-bcPublie un artefact par module
--artifact-dir DIRRacine des artefacts (exige --emit)
-o PATH / --output PATHLie un exécutable (édition de liens) ; incompatible avec --emit
--linker PATH, --runtime-library PATHPilote Clang et archive du runtime pour -o
--no-cacheNe lit ni n'écrit les caches de build
--cache-inference-in-dirs PATTERNRépétable : les modules des répertoires correspondants gardent leurs résultats d'inférence dans le cache de build, en plus des répertoires d'applications OTP implicites (cache d'inférence)
--build-dir DIROù Clause garde ses fichiers temporaires (sous DIR/tmp) ; remplace le build_dir de chaque cible de projet sélectionnée ; sans cette option, les entrées positionnelles n'en ont pas (répertoire de build)
--entry MODULE[:FUNCTION[/ARITY]]Fonction d'entrée/1 de l'exécutable (sinon fonction/0 ; /0 ou /1 en sélectionne une) ; validée dans tous les modes de compilation et ajoute l'artefact de démarrage clausev1_start (exécutables) ; les inspections d'IR et d'assembleur laissent alors de côté ce qu'elle ne peut atteindre (vivacité des exports)
--target-triple TRIPLEMachine cible ; --target sert à sélectionner une cible de projet
-O0 / -O2 / -OsCode générique par défaut + LLVM O0 / spécialisation bornée + LLVM O2 / LLVM Os, sans spécialisation, une section par symbole et élimination par l'éditeur de liens du code et des données non référencés
--no-type-specializationDésactive les variantes quel que soit l'ordre des options
--jobs <n>Optimise et émet les modules objets sur n fils d'exécution (par défaut : tous les fils matériels), chaque module dans son propre contexte LLVM ; les objets sont identiques quel que soit le nombre ; --jobs 1, les inspections d'IR et d'assembleur gardent tous les modules dans un même contexte
--ltoLie les modules sous forme de bitcode avec l'optimisation à l'édition de liens de LLD (cibles Windows MSVC et ELF ; exige -o ou un build de projet qui lie) (édition de liens)
--keep-exportsGarde chaque export d'un exécutable appelable au lieu de laisser de côté ceux qu'il ne peut atteindre ; exige -o, un build de projet qui lie ou une inspection d'IR/d'assembleur (vivacité des exports)
--shellLie le shell Erlang interactif et chaque module de bibliothèque, en gardant chaque export appelable ; sans --entry, l'exécutable démarre le shell, donc clau --shell -o erl sans entrées construit un shell sur la seule bibliothèque ; exige -o ou un build de projet qui lie (shell interactif)
--fips=PATHFixe le programme en mode FIPS avec le fournisseur FIPS d'OpenSSL fourni par l'utilisateur dans PATH (module et fipsmodule.cnf, copiés à côté de l'exécutable) ; implique --with-crypto ; un fournisseur absent ou mal formé fait échouer le build (FIPS)
--with-crypto[=PATH]Lie l'archive crypto du runtime et ajoute le module crypto de la bibliothèque au lot, pour les programmes qui n'atteignent crypto que par appels dynamiques (les programmes qui l'appellent sont de toute façon liés avec lui) ; PATH est une installation ou un arbre source d'OpenSSL ≥ 3.1.2 à lier à la place de celui avec lequel Clause a été construit ; exige -o ou un build de projet qui lie (crypto)
-gTables de lignes Erlang dans les objets et les exécutables liés (CodeView/PDB sur les cibles MSVC, DWARF ailleurs) (débogage)
--print-ir / --print-optimized-irIR vérifié avant/après les passes LLVM, avec les lignes du source Erlang en commentaires
--print-asmL'assembleur cible de chaque module après le pipeline sélectionné, chaque suite de code machine commentée par l'instruction IR et la ligne Erlang dont elle provient (listings d'assembleur)
--print-typesChaque module sous forme de source Erlang annoté des types inférés (sémantique) ; s'arrête avant LLVM
--verboseÉvénements de phase [pp], [parse] et [comp] sur stderr, puis le résumé [index] ... de l'index des répertoires
--print-stage-times[time] <stage> <subject>: <ms> ms sur stderr : plan par manifeste de projet (son chargement et l'expansion des motifs de chaque cible sélectionnée), preprocess, parse et transform (modules avec parse transforms) par fichier source, compile par lot (nombre de modules, cible de projet), link par exécutable ; (cached) suit une étape qui a réutilisé un résultat en cache. Avant le total compile d'un lot viennent ses phases avec le même sujet : index, calls, liveness (exécutables et inspections avec un point d'entrée), declared-types, inference-cache (avec N of M covered modules reused, cache d'inférence), inference, inference-store, contracts, lower, optimize, emit (celles qui se sont exécutées ; avec une sortie objet sur plusieurs fils --jobs, optimize déplace aussi chaque module dans son propre contexte LLVM et le vérifie, et emit vérifie et émet les modules en parallèle). Après inference : des lignes inference pass N avec les pas de travail de la passe rapportés au budget de travail de l'inférence (N of BUDGET work steps) et la façon dont elle s'est terminée (inputs changed, fun results changed, settled, ou la passe finale après des passes non stabilisées), en nommant la fonction en cours d'exécution lorsque le budget s'est épuisé, puis (chaque passe après la première) ; skipped: N components, W work steps : composants conservés depuis leur dernier parcours parce que rien de ce qu'ils lisent n'a changé et travail qu'avaient coûté ces parcours (compté dans les pas de travail de la passe), puis ; N lattice joins exécutées par la passe (celles imbriquées comprises), avec , costly: C components given term() inputs lorsque des composants récursifs coûteux ont perdu leurs entrées locales (inférence), puis (chaque passe qui a collecté des entrées locales) ; inputs: F functions, S slots changed (kinds) avec le genre de changement de chaque emplacement d'entrée modifié (first depuis aucune entrée, integer bound, union member au niveau supérieur, shape, deeper pour l'imbrication de conteneurs, function types, term()) et N fun reads not final (valeurs fun F/A qui ont lu un résumé modifié par la suite), puis ; graph: N nodes (+M), W widenings depuis la passe précédente et ; clean: C of K components (A inputs differ, B callees differ), X changed, T ms (P% of pass), S work steps : composants dont les entrées des membres et les résumés des appelés directs sont égaux à ceux de la passe précédente, ceux parmi eux dont les résumés ont tout de même changé, et le temps et le travail qui leur ont été consacrés ; puis inference lattice (sans durée) avec le nombre d'exécutions de chaque opération de treillis sur toutes les passes (join de deux membres, celles imbriquées comprises, widen, meet, within, subtract, truncate) et le nombre d'appels qui répètent une opération avec les mêmes opérandes plus tôt dans leur passe et dans toute passe (les réponses que garde le graphe les servent, et les opérations imbriquées qu'une réponse avait exécutées ne sont ni réexécutées ni recomptées), puis les jointures d'un nombre de membres autre que deux et les recherches d'imbrication de conteneurs, avec celles dont le graphe a gardé la réponse (suivre les répétitions ralentit l'inférence profilée d'environ 7 %) ; puis inference churn avec le temps passé à collecter les entrées et le nombre d'emplacements d'entrée locaux modifiés une fois, deux fois, ... sur toutes les passes, et inference churned <module>:<function>/<arity> #<argument> (sans durée) pour les 10 emplacements les plus modifiés avec le genre de leur dernier changement et leurs faits avant et après celui-ci (chacun limité à 200 caractères) ; puis (sans durée) jusqu'à 10 composants inference clean-changed <module>:<function>/<arity> qui étaient inchangés dans leurs dépendances mais ont changé, avec la passe qui l'a observé la première et le nombre d'occurrences (une dépendance autre que les entrées et les appels directs, comme fun F/A) ; puis (sans durée) inference facts N après chaque passe qui a collecté des entrées : les faits de chaque résumé et entrée locale, leur taille d'arbre maximale et moyenne (nœuds comptés une fois par chemin), l'union la plus large, le nombre atteignant les budgets de profondeur de conteneur, de membres d'union et d'éléments de tuple/map, les résumés ayant le plus (32) de types fonction et les faits dépendants ; puis inference largest avec le plus grand fait de chacun des 3 composants qui ont pris le plus de temps (limité à 200 caractères) ; puis inference slowest <module>:<function>/<arity> pour les 10 fonctions (les groupes récursifs nomment un membre and N more) qui ont pris le plus de temps sur l'ensemble des passes, avec leurs pas de travail et la part de ce travail dans le budget des passes qui les ont exécutées (877450 work steps, 43.9% of 10 passes of 200000 budget), widened to term() lorsqu'un budget a élargi l'un de leurs faits, et les jointures de treillis que leurs parcours ont exécutées et les nœuds du graphe qu'ils ont créés ; puis inference costliest <module>:<function>/<arity> pour les 10 fonctions d'au moins 10 ms qui ont pris le plus de temps par pas de travail, avec ce temps en microsecondes, leurs pas de travail, jointures de treillis (et jointures par pas de travail) et nœuds créés ; puis inference rounds <module>:<function>/<arity> and N more pour les 10 groupes récursifs les plus lents : résolutions sur l'ensemble des passes, tours exécutés par rapport au plafond par résolution (8 + 4 par membre), résolutions ayant atteint le plafond ou dépensé leur huitième du budget de la passe (chaque membre élargi à term()), résolutions abandonnées d'emblée parce qu'une passe antérieure l'avait fait (remembered) ou terminées avec le budget de la passe épuisé, parcours de membres (et ceux sautés parce que rien de ce qu'ils lisent n'avait changé, et ceux rejoués : une résolution ultérieure a repris le dernier parcours du membre parce que rien de ce qu'il lisait n'avait changé) et la part des parcours et rejeux qui a changé un résumé (agrandi, entrées de type fonction changées, ou revenu à l'un des 4 derniers résumés du membre), et les membres changés par tour de la dernière résolution en dix groupes de tours, puis ; N solves repeated the previous fixed point, M reached it after R rounds in total : résolutions terminées sur les résumés avec lesquels la passe précédente s’était terminée, et résolutions les ayant atteints avant de converger, avec les tours nécessaires (ce que partir de ces résumés permettrait de sauter). Une compilation en cache n'affiche aucune phase
--asn1-option OPTION, --asn1-outdir DIR, --generate-asn1Compile les entrées .asn/.asn1/.set.asn en Module.erl et Module.hrl avec une option asn1ct (répétable), dans DIR (ajouté au chemin d'inclusion), ou les génère seulement (ASN.1)
--print-envAffiche l'environnement de compilation résolu en TOML sur stdout et termine sans traiter les sources (rapport d'environnement)
--print-inputsPrétraite et analyse les entrées, ajoute les modules qu'elles nomment comme le fait la compilation, puis liste chaque source du lot sur stdout et s'arrête avant la compilation (projets)

Listings d'assembleur

--print-asm exécute le pipeline sélectionné (comme --print-optimized-ir), puis affiche l'assembleur de chaque module d'entrée depuis la même machine cible que celle qui écrit les objets, un module à la fois. Les commentaires utilisent le marqueur de la cible (# sur x86, // sur AArch64 ELF, ; sur Apple, @ sur 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

Rapport d'environnement

--print-env valide toute la ligne de commande (conflits, entrées positionnelles lisibles, planification du projet), puis affiche ce que cette commande utiliserait sous forme d'un document TOML sur stdout, termine avec le code 0 et ne lit aucun source. Les chemins sont absolus, avec /.

# 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"

(Les tableaux affichent un élément par ligne.) actions liste les actions du frontend (print-pp, parse-check, print-ast, print-source, print-abstr, sinon preprocess-check ; print-inputs seul les remplace), sinon les inspections (print-types, print-ir, print-optimized-ir, print-asm), sinon emit, link (-o) ou compile. Dans un build de projet, les cibles ayant un output lient aussi. Les modules trouvés plus tard par leur nom (via source_search_paths ou la bibliothèque) ne sont pas listés : les trouver exige l'analyse syntaxique (ce que fait --print-inputs). --print-env ne peut pas être combiné avec --new-project.

Cache de compilation

Un lot disposant d'un répertoire de build (chaque cible de projet, les entrées positionnelles avec --build-dir) garde le résultat de sa compilation dans le cache de build : les diagnostics dans l'ordre, l'échec et, lorsqu'un exécutable ou --emit en a besoin, les objets (ou le bitcode) de chaque module et du module de démarrage. Un build ultérieur du même lot avec les mêmes options rejoue les diagnostics et lie ou publie les objets conservés exactement comme des objets neufs ; --print-stage-times affiche alors [time] compile ... (cached). --print-ir, --print-optimized-ir, --print-asm et --print-types recalculent toujours, et --no-cache ne lit ni n'écrit rien.

Backend

SDK LLVM

LLVM stable 23.1.x, au minimum 23.1.1. LLVM est une dépendance de l'hôte : l'architecture, la bibliothèque standard C++ et le CRT Windows doivent correspondre à ceux de l'outil de compilation. Le code du projet conserve les exceptions/RTTI ; aucune exception ne doit traverser LLVM lors du déroulement de la pile. Le runtime n'utilise jamais LLVM.

Configuration Windows x64 de référence : clang-cl 23.1.2 de l'hôte dans C:/Program Files/LLVM/bin, SDK thirdparty/clang+llvm-23.1.2-x86_64-pc-windows-msvc, outils Visual Studio 18 x64, Windows SDK 10.0.26100.0, Ninja, /MT, _ITERATOR_DEBUG_LEVEL=0. La sélection automatique applique /MT et les réglages d'itérateur ; un LLVM_DIR explicite ne le fait pas, et la sonde d'édition de liens échoue alors.

PCRE2

Les builtins re du runtime utilisent PCRE2 10.47, la version qu'embarque OTP 29 (erts/emulator/pcre/vendor.info), de sorte que la syntaxe des motifs, les options et les captures se comportent comme dans OTP : unités de code de 8 bits, prise en charge d'Unicode, sans JIT, pcre2_match d'origine (le correctif de cession d'OTP n'est pas repris). PCRE2 est sous licence BSD-3-Clause avec l'exception PCRE2 pour les binaires compilés ; aucun fichier de PCRE2 n'est versionné. CMake (cmake/Pcre2Dependencies.cmake) le résout dans cet ordre :

  1. -DCLAUSE_PCRE2_ROOT=<source directory> est prioritaire (aucun téléchargement) ; la version de son configure.ac doit être 10.47.
  2. Un PCRE2 installé d'au moins la version 10.47 (CLAUSE_PCRE2_SYSTEM=ON, la valeur par défaut) : le paquet CMake (find_package(pcre2 CONFIG COMPONENTS 8BIT), bibliothèques statiques préférées), sinon pkg-config libpcre2-8, une libpcre2-8.a statique étant préférée à la bibliothèque partagée.
  3. La version épinglée, téléchargée dans thirdparty/pcre2-10.47 et vérifiée (pcre2-10.47.tar.bz2, SHA-256 47fe8c99461250d42f89e6e8fdaeba9da057855d06eb7fc08d9ca03fd08d7bc7) ; -DCLAUSE_DOWNLOAD_PCRE2=OFF désactive le téléchargement.

Un répertoire source (1 ou 3) construit la bibliothèque d'objets clause_pcre2 à partir des fichiers .c listés, avec les fichiers distribués config.h.generic, pcre2.h.generic et pcre2_chartables.c.dist copiés dans l'arbre de build (aucune étape de génération, aucune option d'avertissement du projet, hors de clang-tidy et de Lizard), et ses objets sont ajoutés à clause_runtime lui-même : l'archive est autonome et chaque consommateur (éditions de liens de clau, programmes de test imbriqués, runtime_link, examples/compile) obtient PCRE2 avec elle. Une bibliothèque installée devient à la place une entrée supplémentaire de l'éditeur de liens, enregistrée dans clause_runtime.link à côté de l'archive (une entrée par ligne), que clau ajoute à chaque édition de liens ; une bibliothèque partagée rend les exécutables dépendants d'elle à l'exécution. Par plateforme : Windows utilise le téléchargement (aucun PCRE2 système n'y est recherché en pratique) ; Linux et macOS utilisent un PCRE2 10.47 ou plus récent installé s'il est présent, sinon le téléchargement. PCRE2 est toujours lié dans le runtime, y compris dans les programmes qui n'appellent jamais re.

zlib

Le module zlib du runtime et les termes externes compressés utilisent zlib 1.3.2, la version qu'embarque OTP 29 (erts/emulator/zlib/vendor.info), de sorte que les octets compressés sont identiques à ceux d'OTP. CMake compile ses sources de bibliothèque (cmake/ZlibDependencies.cmake, clause_add_runtime_zlib) dans la bibliothèque d'objets clause_runtime_zlib, dont les objets rejoignent clause_runtime comme ceux de PCRE2 ; -DCLAUSE_RUNTIME_ZLIB_ROOT=<source directory> est prioritaire, sinon la version est téléchargée dans thirdparty/zlib-1.3.2 et vérifiée (SHA-256 bb329a0a2cd0274d05519d61c667c062e06990d72e125ee2dfa8de64f0119d16) sur chaque hôte. La licence de zlib est la licence zlib. Le LLVM du compilateur garde sa propre zlib ; les deux ne se rencontrent jamais dans un même binaire.

zstd

Le module zstd du runtime utilise zstd 1.5.7, la version qu'embarque OTP 29 (erts/emulator/zstd/vendor.info), de sorte que les trames sont identiques à celles d'OTP. CMake compile ses sources de bibliothèque (common, compress, decompress ; monothread, sans le décodeur en assembleur ; cmake/ZstdDependencies.cmake, clause_add_runtime_zstd) dans la bibliothèque d'objets clause_runtime_zstd, dont les objets rejoignent clause_runtime comme ceux de zlib ; -DCLAUSE_RUNTIME_ZSTD_ROOT=<source directory> est prioritaire, sinon la version est téléchargée dans thirdparty/zstd-1.5.7 et vérifiée (SHA-256 eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3), la même archive qu'utilise le build LLVM de Windows. La licence de zstd est BSD-3-Clause (ou GPL-2.0).

OpenSSL

Les builtins crypto du runtime utilisent libcrypto d'OpenSSL, épinglé à 4.1.0, toute version depuis 3.1.2 (la dernière avec un fournisseur validé FIPS) étant acceptée, dans une archive clause_runtime_crypto séparée que seuls les programmes appelant crypto lient. Sa provenance, sa compilation depuis les sources par Clause et clau, l'édition de liens (--with-crypto[=PATH]) et le mode FIPS (--fips=PATH) sont décrits dans crypto.

Autres prérequis : CMake ≥ 3.28, un compilateur C++23, Boost ≥ 1.90, toml++ 3.4.0 et les outils de qualité. OTP n'est nécessaire que pour les audits optionnels et la régénération des fixtures.