Clause
← Toute la documentation

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

Exécutables

Contrat des programmes construits par clau -o ou par un build de projet. Les entrées positionnelles, ou exactement une cible de projet sélectionnée, sont liées dans le chemin -o ; un build de projet lie chaque cible exécutable sélectionnée à son output du manifeste (édition de liens, projets).

Choix du point d'entrée

Le point d'entrée est une fonction exportée d'arité 1 qui reçoit la liste des arguments. Une FUNCTION sélectionnée sans FUNCTION/1 peut à la place être une FUNCTION/0 exportée : elle s'exécute sans les arguments du programme (les options du runtime en sont tout de même retirées). Un suffixe /0 ou /1 ne sélectionne que cette arité, donc --entry benchmarks:run/0 exécute run/0 même si run/1 est aussi exportée. Le choix par défaut (sans sélection) est toujours main/1.

SourceÉcriturePortée
CLI--entry MODULE[:FUNCTION[/ARITY]]Lot positionnel, ou l'unique cible de projet sélectionnée
Manifesteentry = "MODULE[:FUNCTION[/ARITY]]" dans une table [[targets]]Cette cible
Par défautL'unique escript, sinon l'unique module exportant main/1Seulement lorsqu'un exécutable est demandé (-o, ou une cible de projet avec output)
ÉchecDiagnostic (code de sortie 1)
Module absent du lot<origin>: entry module M is not among the compiled modules (origine : --entry ou manifeste file:line:col [target t] (entry))
Ni F/1 ni F/0<file>:<line>:<col>: entry function M:F/1 is not defined à la déclaration du module
F/A explicite absente... entry function M:F/A is not defined; found F/N à une définition de même nom, sinon à la déclaration du module
Seulement d'autres arités... is not defined; found F/N, but the entry takes the argument list (FUNCTION/1) or nothing (FUNCTION/0) à cette définition
F/1 (sinon F/0) non exportée<file>:<line>:<col>: entry function M:F/A is not exported à la définition
Pas de sélection, aucun export 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)
Pas de sélection, plusieursambiguous entry point: main/1 is exported by a, b; choose the entry with ... (même indication)

Pour les cibles de projet, l'indication nomme aussi la clé du manifeste : ... --entry MODULE[:FUNCTION[/ARITY]] or with entry = "MODULE[:FUNCTION[/ARITY]]" in this target's [[targets]] table of the project manifest ..., par exemple :

[[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

Arguments

Entry(Argv) reçoit une liste propre de chaînes (listes de points de code Unicode), à l'exclusion du nom du programme et des options du runtime en tête, sinon inchangées et dans l'ordre, comme escript.

Options du runtime

Le runtime lit ses options dans la variable d'environnement CLAUSE_FLAGS (mots séparés par des espaces et des tabulations, sans guillemets), puis dans les arguments de ligne de commande en tête, de sorte que la ligne de commande l'emporte. Les deux sont analysés de la même façon ; une valeur se place dans l'argument suivant ou après =.

OptionEffet
--max-atoms NTaille de la table des atomes, de 1 à 2^26 (67 108 864) ; 2^20 (1 048 576) par défaut, comme +t d'OTP
--max-heap BYTESPlafond du bloc de tas, des fragments et des binaries hors tas de chaque processus, au moins le tas minimal (233 mots) ; non plafonné par défaut
--max-stack BYTESPlafond de la pile de cadres de chaque processus ; non plafonné par défaut
--min-heap-size WORDSmin_heap_size par défaut de chaque processus, au moins 1, arrondi à une taille de tas supérieure, comme +hms d'OTP ; au plus --max-heap ; 233 par défaut (dimensionnement)
--max-memory BYTESPlafond de la mémoire de l'ensemble des processus (tas, binaries hors tas, piles) ; non plafonné par défaut
--schedulers NThreads de travail de l'ordonnanceur exécutant les processus, de 1 à 1 024 ; par défaut, un par CPU de --scheduler-cpus, sinon un par processeur logique sur lequel le programme est autorisé à s'exécuter (son masque d'affinité), comme les ordonnanceurs en ligne d'OTP (threads de travail)
--scheduler-cpus SETLie le thread de travail N au N-ième CPU de SET, de façon cyclique, comme +sbt/+sct d'OTP : performance (la classe de cœurs la plus rapide : les P-cores d'un CPU hybride, tous les CPU sinon), all, ou une liste telle que 0-15 ou 2,4,6 ; chaque CPU listé doit être dans le masque d'affinité. Sans liaison par défaut (liaison aux CPU)
--profile FILEÉcrit dans FILE, à la fin du programme, un rapport des entrées et du temps propre par fonction et par processus (profilage) ; désactivé par défaut
--args-file FILEFichier d'options comme vm.args d'OTP : réservé, rapporte runtime option --args-file is not implemented
--Termine les options du runtime ; les arguments suivants vont tous à Entry

Sur la ligne de commande, l'analyse s'arrête au premier argument qui n'est pas une option du runtime, donc prog data --max-atoms 9 transmet les trois arguments au programme. Dans CLAUSE_FLAGS, chaque mot doit être une option du runtime. Une valeur invalide, un mot de la variable qui n'est pas une option, ou --args-file arrête le programme avant l'enregistrement de tout module : clau: runtime failure: <reason>, code de sortie 70. Le nombre de processus n'est pas limité et la mémoire n'est pas plafonnée par défaut. Les valeurs en octets sont décimales, sans suffixe, et arrondies à l'inférieur en mots entiers ; un programme qui atteint un plafond échoue avec resource_limit, code de sortie 70 (limite mémoire du runtime).

Code de sortie

RésultatCode
Le point d'entrée retourne (n'importe quelle valeur)0
erlang:halt()0
erlang:halt(N), entier non négatifN (les hôtes POSIX gardent les 8 bits de poids faible)
erlang:halt(Slogan) avec une chaîneSlogan sur stderr, puis 1 (pas de crash dump)
erlang:halt(abort)Abandon natif (sans vidage des tampons)
Toute exception qui s'échappe du point d'entrée, y compris throw et exit(normal)Rapport sur stderr, 1
Processus d'entrée terminé par un signal de sortie (processus)Rapporté comme un exit non capturé, 1 ; raison normal : 0
Échec du démarrage du runtime ou de l'infrastructure (incompatibilité d'ABI, enregistrement, mémoire avant le point d'entrée)Message sur stderr, 70
Mémoire de l'hôte épuisée (croissance du tas, d'un binary hors tas ou de la pile refusée ; aucun plafond mémoire par défaut, épuisement de la mémoire)clau: runtime failure: entry call failed: out_of_memory, 70

Des arguments invalides de halt/1 lèvent badarg chez l'appelant. Lorsque le point d'entrée se termine, le programme se termine : les autres processus sont arrêtés sans poursuivre leur exécution, comme avec halt/1 d'OTP après le retour d'escript. Un halt dans n'importe quel processus termine le programme avec son code, et un échec du runtime dans n'importe quel processus le termine avec 70 ; une exception dans un processus créé par spawn ne termine que ce processus (processus).

halt/0,1, error/1,2,3, exit/1 et throw/1 peuvent être appelées avec ou sans le préfixe erlang: ; une définition locale ou -compile({no_auto_import, ...}) garde le nom non qualifié local, comme dans OTP. halt/2 n'est pas disponible. halt(N) garde les 31 bits de poids faible de tout entier non négatif, comme le fait OTP. Un slogan est une liste propre d'au plus 1 023 points de code Unicode. Un halt déroule le point d'entrée à travers le canal d'erreur vérifié comme une erreur, il n'arrête donc le programme qu'après le nettoyage généré.

Flux de sortie

Objet de démarrage

Compiler avec un point d'entrée explicite (--entry ou entry du manifeste) ajoute un module de démarrage après les modules du lot. Avec --emit, il est publié sous le nom clausev1_start.{obj,o,ll,bc} à côté des artefacts des modules (le nom ne peut pas entrer en collision avec un artefact de module). Il contient un abi::v1::StartupDescriptor constant (startup.hpp) : révision de l'ABI, largeur de terme, chaque descripteur de module dans l'ordre des sources, les noms du module et de la fonction d'entrée et un indicateur escript. Son int main(int, char **) appelle CLAUSE_main_v1 du runtime, qui :

  1. Vérifie la révision de l'ABI et la largeur du descripteur de démarrage et de chaque descripteur de module avant tout enregistrement ; une incompatibilité sort avec le code 70.
  2. Démarre le runtime et enregistre tous les modules ; tout échec s'arrête avant le point d'entrée et abandonne le runtime (code de sortie 70), donc aucun code Erlang ne s'exécute sur un lot partiel.
  3. Crée le processus d'entrée, construit argv et met en file l'appel de M:F/1 comme processus principal, puis l'exécute, ainsi que chaque processus qu'il crée, sur les workers de l'ordonnanceur (processus) jusqu'à sa fin.
  4. Convertit le résultat en code de sortie ci-dessus, en affichant les rapports après le vidage de stdout, puis libère chaque processus et arrête le runtime sur chaque chemin (sauf halt(abort)).

clau -o lie lui-même ces objets (édition de liens). Édition de liens manuelle (la recette du harnais natif sans source de harnais) :

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

Toute édition de liens compatible Clang des objets avec Clause::generated_program fonctionne de la même façon (voir tests/compiler/linking/startup.cmake).

Édition de liens

clau [-O0|-O2|-Os] -o PATH a.erl b.erl ... (ou --project FILE [--target T] -o PATH pour une cible sélectionnée) compile le lot en mémoire, ajoute l'objet de démarrage pour le point d'entrée et lie un exécutable :

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

-Os place en outre chaque fonction et objet de données, générés ou du runtime, dans sa propre section et lie avec --gc-sections (ELF), -dead_strip (Mach-O) ou /OPT:REF /OPT:ICF (MSVC), de sorte que le code qu'aucun chemin depuis le point d'entrée n'atteint est supprimé.

--lto compile les modules et le module de démarrage en bitcode LLVM au lieu d'objets et lie avec -flto -fuse-ld=lld, de sorte que LLD optimise le code généré du programme comme une seule unité face à l'archive native du runtime. Les descripteurs de modules et de démarrage, les entrées exportées et main restent référencés et subsistent ; les programmes se comportent comme sans cette option. Seul LLD lit le bitcode de ce compilateur, donc --lto est accepté pour les cibles Windows MSVC et ELF ; les autres échouent avant l'édition de liens avec --lto is not supported for target <triple>: it needs LLD. --lto exige une sortie exécutable (-o ou un build de projet qui lie). Le Clang/LLD installé doit être de la même version de LLVM que clau (23.1).

Vivacité des exports

Un exécutable laisse de côté les exports qu'il ne peut pas atteindre, de sorte que l'élimination du code mort de -Os et --lto suppriment le code de bibliothèque que le programme n'appelle jamais. Juste après la résolution des appels, avant l'inférence de types, une analyse du lot entier part de la fonction d'entrée et marque comme vivant tout ce qu'une fonction vivante appelle directement (y compris via -import et les auto-imports d'OTP), chaque fun F/A et fun M:F/A littérale, et les cibles littérales de apply/3, spawn/3, spawn_link/3, spawn_monitor/3, spawn_opt/4,5, spawn_request/3,5, hibernate/3 et make_fun/3 (toutes les arités de M:F lorsque la liste d'arguments n'est pas littérale), ainsi que file:write/2 dès que io:format/3, io:fwrite/3, io:put_chars/2 ou io:nl/1 est vivant (leurs builtins écrivent par son intermédiaire vers un périphérique de processus). Elle reste conservatrice :

Une fonction qu'aucun code vivant n'atteint n'obtient aucun code. Un export laissé de côté garde son nom dans la table d'exports du module, sans code, de sorte que function_exported/3 et module_info(exports) répondent comme s'il était exporté, tandis qu'un appel dynamique qui l'atteint lève undef (différences). Cela ne peut arriver que lorsqu'un nom de module vient de l'extérieur de l'analyse : une trace de pile, un terme d'exception ou des données venant de l'extérieur du programme. --verbose trace le résultat par module (phase=liveness detail="dropped-exports=3", ou detail="kept-all-exports reason='m' is used as a value in main:main/1").

Une fonction qu'aucun code vivant n'atteint n'est jamais non plus soumise à l'inférence de types ni vérifiée contre sa -spec : l'inférence ne couvre que le code vivant, ce qui est plus rapide pour les programmes qui utilisent peu de gros modules de bibliothèque et ne peut que restreindre les faits des fonctions vivantes, puisque les appelants inaccessibles ne joignent plus leurs arguments. Une spécification contredite uniquement dans du code inaccessible est donc signalée pour les artefacts --emit mais pas pour l'exécutable.

--keep-exports (ou l'option de projet keep_exports = true) garde chaque export appelable. L'analyse s'applique aux exécutables (-o ou un build de projet qui lie) et aux inspections d'IR et d'assembleur dotées d'un point d'entrée (--entry ou l'entry du manifeste de la cible), qui montrent alors le code de l'exécutable ; les artefacts --emit, destinés à d'autres programmes, gardent chaque export.

-g garde les tables de lignes Erlang pour les débogueurs : dans l'exécutable (ELF), ou dans un <name>.pdb publié à côté (cibles MSVC) (débogage).

Échec (code de sortie 1)Diagnostic
Pas de Clangcannot find clang++ or clang on PATH; install LLVM/Clang or pass --linker / linker not found: X
Pas de runtimeruntime library not found: P; build the clause_runtime target or pass --runtime-library
Pas une archiveruntime library is not a static library: P: ...
Mauvaise cibleruntime library P contains x86_64 coff objects, but the executable targets T; ...
Erreur d'édition de lienslinking O failed: <clang> exited with status N: suivi de la sortie de l'éditeur de liens (64 Kio au plus)
Destination incorrecteoutput directory does not exist: D, artifact destination is not a regular file: O, artifact destination aliases an input: O

Escripts

Un fichier source dont la première ligne commence par #! est compilé comme un escript, dans n'importe quel mode et quel que soit le nom du fichier (entrées positionnelles ou fichiers .erl de projet). Les règles suivent escript d'OTP 29 pour les scripts sources :

Comparaison avec OTP

Les goldens des fixtures de programmes exécutent le point d'entrée sous OTP avec les mêmes règles (oracle) ; le même oracle génère les cas golden d'exécutables que l'exécuteur de bout en bout lie et exécute sous chaque politique. Différences avec escript pour les modules ordinaires : les exceptions non capturées sortent avec 1 au lieu de 127, et main/1 doit être exportée. Les sources escript gardent les règles d'OTP (voir ci-dessus).