Class flixw


public final class flixw extends Object
Stage 0 of the flixw bootstrap: one file, no dependencies, Java 21.

It owns project discovery, lock parsing, drift detection, version validation, Java selection, compiler acquisition, unconditional digest verification, compiler-first verb dispatch, the wrapper's own verbs, and the process launch. The two shims that reach it, flixw and flixw.cmd, own exactly one decision each -- which java -- plus one cache lookup, because logic in a shim has to be written twice and cannot be unit-tested.

The stock Flix compiler is never modified, patched, or linked against. It is fetched by URL, verified against a SHA-256 committed in .flixw/lock.toml, and executed as an opaque process. The digest is recomputed on every invocation: there is no install stamp and no flag that skips it.

These docs are published from the flixw repository and cover every member, private ones included, because the internals are what a reader has to trust before letting this file download and run a compiler. docs/CONTRACT.md is the description of what ships and what is promised; this is how it is done.

See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    (package private) static final record 
    One release asset: what to fetch, and what the publisher says it hashes to.
    (package private) static final class 
     
    (package private) static final record 
     
    (package private) static final record 
     
    (package private) static final record 
     
    (package private) static final record 
    One key in lock.toml: the table it lives in, whether that table may omit it, the shape its value must have, and the sentence a diagnostic uses to describe it.
    (package private) static final record 
    What one `pin` command line asks for; `parsePin` is the only thing that builds it.
    (package private) static final record 
    Whether the lock was rewritten, and the sentence explaining why not when it was not.
    (package private) static final record 
    One `key = value` occurrence, the table it was found in, and the line it sits on.
    (package private) static final record 
    Every scalar entry in a document, plus every table header, in file order.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    (package private) static final String
     
    (package private) static final String
     
    (package private) static final List<String>
    Fallback verb set, observed in Flix 0.75.1 and 0.75.2.
    (package private) static final String
     
    (package private) static final Pattern
    Two-space indent, a lowercase name, then the column gap before its description.
    (package private) static final String
     
    (package private) static final String
     
    (package private) static final String
    The note naming the pinned compiler's own completion script, when it ships one.
    (package private) static final String
     
    (package private) static final String
     
    (package private) static final List<String>
     
    (package private) static final String
    A template for the one supported way to run a compiler flixw did not download.
    (package private) static final String
    flixw's own releases.
    (package private) static final int
    How much of a help screen counts as the header for parseReportedVersion(java.lang.String).
    (package private) static final int
     
    (package private) static final Duration
     
    (package private) static final String
    A feature release or an exact one, and nothing else -- no ranges, no vendor.
    (package private) static final String
    `.flixw/local/` holds what only this machine knows -- currently the resolved JDK -- and must not be committed.
    (package private) static final List<flixw.LockField>
    Every key a lock may hold, in the order a generated lock writes them.
    (package private) static final String
    The URL written into every generated lock as a `#:schema` directive, and the `$id` of the schema itself.
    (package private) static final String
    The lock format's major version, which is not the wrapper's.
    (package private) static final int
    Adoptium answers in a few tens of KiB; this is room to spare, not a target.
    (package private) static final int
     
    (package private) static final Set<String>
    Locks already reported on, so a second read in the same run stays quiet.
    (package private) static final String
    Where the generated documentation and the JSON Schema are published.
    (package private) static final String
    One usage line for `pin`, because four diagnostics quote it and the fourth was already a release behind the first the last time one was written out by hand.
    (package private) static final Duration
    Bounds for the two child processes stage 0 runs for information rather than for work.
    (package private) static final String
    GitHub's own limits on the two path segments; a fork may live anywhere within them.
    (package private) static final Pattern
     
    (package private) static final String
     
    (package private) static final List<String>
     
    (package private) static final int
    The oldest javac that can compile this file, which is a different number from the floor above and answers a different question.
    (package private) static long
     
    (package private) static final int
    The interval flixw is tested on.
    (package private) static final Pattern
     
    (package private) static final String
    Where the stock compiler comes from when nothing says otherwise.
    (package private) static final String
    The name of the note stage 0 leaves for a completer, holding the verbs this project would actually dispatch.
    (package private) static final Pattern
    A version token standing on its own, rather than one buried inside a longer word.
    (package private) static final String
     
    (package private) static final List<String>
     
    (package private) static final String
     
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    (package private) static boolean
    acceptable(int f, String source)
    Below MIN_JAVA is always fatal: the compiler will not run.
    (package private) static Path
     
    (package private) static String
    The reported version for the verbs that print state; null whenever there is no jar.
    (package private) static boolean
    Does this release asset exist? A HEAD, so the download itself stays a single attempt.
    (package private) static int
    Waits for a child that owns the terminal, and guarantees it dies with us.
    (package private) static int
    How much this line opens or closes an inline array, counting only brackets outside quotes.
    (package private) static String
    The version record verbs(java.nio.file.Path, java.nio.file.Path, java.lang.String) already wrote, if any -- a file read, never a subprocess.
    (package private) static Path
     
    (package private) static String
    The single normalization used for release tags, cache coordinates, and every version comparison.
    (package private) static String
    The line endings the flixw block pins for one shipped path.
    (package private) static String
    captureHelp(Path javaExe, Path jar)
    The compiler's `--help`, bounded, as text.
    (package private) static List<String>
     
    (package private) static int
    check(Path root, flixw.Lock lock, Path jar, flixw.Jvm jvm)
    Every check, printed; the count is the caller's to act on.
    (package private) static int
    checkCanonical(Path file, String canonical, String label)
    Compares a committed invariant file against the bytes this wrapper release ships.
    (package private) static int
    gitattributes resolves by *last* matching pattern, so a rule after the wrapper block silently overrides it -- and a checked-out shim with the wrong line endings is exactly the failure the block exists to prevent.
    (package private) static String
    checkRepo(String repo, String where)
     
    (package private) static flixw.Jvm
    chooseInstall(List<flixw.Jvm> candidates, boolean strict)
    Picks among discovered installations: the newest JDK that is still inside the tested interval, and only if none is, the one just above it.
    (package private) static Path
    compilerCompletion(Path javaExe, Path jar, String identity, List<String> verbs)
    The pinned compiler's own completion script, cached, or null if it has none.
    (package private) static Path
     
    (package private) static String
    A completion script for one shell, on stdout.
    (package private) static int
    count(String haystack, String needle)
     
    (package private) static void
     
    (package private) static void
    download(String url, Path dest)
     
    (package private) static String
    A release tag in a URL path.
    (package private) static String
     
    (package private) static Path
    exeIn(String home)
     
    (package private) static flixw.Fail
    fail(String code, int exit, String msg)
     
    (package private) static Integer
     
    (package private) static String
     
    (package private) static Path
    Layout differs per platform -- macOS nests a .jdk bundle -- so look rather than guess.
    (package private) static Path
    findRoot(Path anchor)
    Search upward from cwd for flix.toml, bounded above by the wrapper's own project.
    (package private) static Integer
    git(Path root, String... args)
    Runs git <args> in root; null when git is absent or the command fails to start.
    (package private) static HttpClient
    The one HTTP client, pinned to HTTP/1.1.
    (package private) static String
    One bounded HTTPS GET returning text.
    (package private) static String
    humanSize(long bytes)
    A byte count as a person reads it; cached JARs and JDKs are always well above 1 KB.
    (package private) static boolean
    True when a path names something inside the cache flixw fills with pinned compilers.
    (package private) static void
    install(Path target, Path source)
     
    (package private) static Path
    The java recorded by the last successful install, if it is still there.
    (package private) static Path
    Downloads, verifies and unpacks one JDK into the wrapper cache, and returns its `java`.
    (package private) static void
    `./flixw wrapper --install-jdk`, so the choice need not wait for a failure.
    (package private) static boolean
    True when an entry is `table.key`.
    (package private) static boolean
     
    (package private) static boolean
     
    (package private) static boolean
    Is there a JDK on this machine that satisfies the pin? Asked by `pin` so that writing one is not silently a decision to break the next command.
    (package private) static String
    aarch64 or x64 as Adoptium spells it, or null where it publishes nothing for us.
    (package private) static String
    Windows gets a zip; nobody publishes a tar.gz for it.
    (package private) static void
    jdkInstructions(int want)
    What to type on this OS, pointing at the same vendor flixw would fetch.
    (package private) static String
     
    (package private) static String
    jsonField(String json, String key)
    Enough JSON for flat string fields of one small, known response.
    (package private) static String
    jsonObject(String json, String key)
    The first brace-balanced object under `"key":`.
    (package private) static String
    JSON string literal.
    (package private) static List<String>
     
    (package private) static List<Path>
    Directories a JDK is commonly unpacked into.
    (package private) static void
    launch(Path javaExe, List<String> opts, Path jar, List<String> args)
    Inherit cwd and the three streams; propagate the child's status.
    (package private) static void
    Everything already sitting on this machine -- compilers and JDKs flixw itself cached, plus the JDKs knownInstalls() can already see without a network call.
    (package private) static List<flixw.LockField>
    The fields declared for one table, in lock order.
    (package private) static Path
    lockPath(Path root)
     
    (package private) static String
    The published JSON Schema for lock.toml, rendered from LOCK_SCHEMA.
    (package private) static List<String>
    The tables the schema knows about, deduplicated, in lock order.
    (package private) static String
    lockText(String wrapper, String repo, String version, String url, String sha256, String java)
    One place that knows what a lock looks like, so the writer cannot drift by table.
    static void
    main(String[] args)
    The one entry point.
    (package private) static String
    The manifest is the human authority; disagreement stops us before the network.
    (package private) static void
     
    (package private) static void
    Moves a project installed under the pre-0.20 names onto the current ones.
    (package private) static flixw.Jvm
    noJavaFound(int self, String pin)
    Nothing usable was found: say how to fix it, then offer to do it.
    (package private) static void
    noteUnknownLockKeys(String text, String where, String wroteIt)
    Keys the schema does not describe, reported once and never fatally.
    (package private) static int
     
    (package private) static boolean
    offerJdk(int want)
    Offers to fetch one only when there is somebody to answer.
    (package private) static boolean
    Is `a` no newer than `b`? Both are the wrapper's own dotted versions.
    (package private) static flixw.Pin
    parsePin(List<String> args, flixw.Lock existing)
    ./flixw pin [<owner>/<repo>] [<version>] [--java <version>], or ./flixw pin --refresh.
    (package private) static String
    The version the compiler says it is, read from the header of its own help.
    (package private) static List<String>
    The verbs a help screen advertises, by three independent parses.
    (package private) static boolean
    patternMatches(String pattern, String path)
    Does one .gitattributes pattern match one path flixw ships?
    (package private) static void
    pin(Path root, flixw.Pin what)
     
    (package private) static int
    probe(Path exe)
    Reads <home>/release when present and parseable; else runs the candidate once.
    (package private) static String
    The candidate's own version string -- `21.0.12` rather than `21` -- so a pin can be as exact as the person who wrote it chose to be.
    (package private) static String
    q(String s)
     
    (package private) static flixw.Lock
    readLock(Path lockFile)
     
    (package private) static Map<String,String>
    Reads every key LOCK_SCHEMA declares, keyed as `table.key` with the root table's keys unprefixed.
    (package private) static void
     
    (package private) static void
    recordCompletion(Path root, Path script)
    Same, for the path to the compiler's own completion script; absent means none.
    (package private) static void
    recordJava(Path root, Path exe)
    Leaves the shim a note saying which JDK this project resolved to, so the next run can start on it instead of starting on whatever `java` is first on PATH and then relaunching.
    (package private) static void
    recordNote(Path root, String name, String body)
     
    (package private) static void
    recordVerbs(Path root, List<String> compilerVerbs)
    Records the verbs this project dispatches, for a completer to read.
    (package private) static String
    Redacts credentials from a URL-shaped value before it is printed.
    (package private) static String
    The same, for JVM option strings, which can carry -Dhttps.proxyPassword=secret.
    (package private) static flixw.Refresh
     
    (package private) static void
    `./flixw pin --refresh`.
    (package private) static boolean
    At most one relaunch, guarded by an env marker, so a stale release file cannot loop.
    (package private) static void
    report(Path root, flixw.Lock lock, Path jar, flixw.Jvm jvm, List<String> cv, String reported)
     
    (package private) static String
    reportedVersion(Path javaExe, Path jar, String identity)
    What the pinned compiler says its version is, or null if it will not say.
    (package private) static void
    Says so when FLIX_JAR names a compiler out of flixw's own cache.
    (package private) static void
    Says so when the compiler is not the version the lock claims.
    (package private) static Path
     
    (package private) static flixw.Asset
    resolveRelease(String repo, String version)
    Resolves the compiler artifact for one repository and version, without asking an API anything.
    (package private) static flixw.JdkPackage
    resolveTemurin(int feature)
    Resolves the current Temurin release for this platform.
    (package private) static void
    restore(Path file, String previous)
    Best-effort rollback; a failed restore must not mask the failure being reported.
    (package private) static String
     
    (package private) static void
    routingNotice(String verb, String compilerVersion)
    Which side handled a verb, under FLIXW_TRACE only.
    (package private) static String
    runCapture(List<String> cmd, Duration timeout, int cap)
    Runs a child and returns its merged output, bounded in both bytes and wall clock.
    (package private) static boolean
    Does this JDK satisfy `[java] version` in the lock? A pin is a prefix of the version, cut at a dot: `21` accepts 21.0.12, `21.0` accepts 21.0.12, and neither accepts 21.1 or 2.
    (package private) static flixw.Jvm
     
    (package private) static void
    selfCompile(Path source)
    Compiles this source into the cache so the shim can skip the JEP 330 source launch next time.
    (package private) static Path
     
    (package private) static String
    sha256(byte[] b)
     
    (package private) static String
    sha256(Path file)
     
    (package private) static Path
    The .java file this stage 0 was launched from, or null when it is running as the compiled class out of the cache.
    (package private) static List<String>
    splitKey(String raw, String where)
    Splits a key into its segments, respecting quotes, then unquotes and trims each one.
    (package private) static Path
    stage0Dir(String srcHash)
     
    (package private) static boolean
     
    (package private) static String
    Strips a trailing comment, ignoring '#' inside quotes.
    (package private) static String
    Accepts the release tag where a version is expected: v0.75.2 means 0.75.2.
    (package private) static String
    tableJson(String table, String indent)
     
    (package private) static List<String>
    One documented tokenizer: whitespace separates; '' and "" quote; \ escapes inside "" and bare.
    (package private) static String
    tomlLookup(String text, String table, String key, String where)
    Reads one key from one TOML table.
    (package private) static flixw.TomlScan
    tomlScan(String text, String where)
    The single TOML line scanner in stage 0.
    (package private) static void
     
    (package private) static boolean
     
    (package private) static String
    The `x.x.x` that `[package].flix` is allowed to hold.
    (package private) static List<String>
    Every key in the file that LOCK_SCHEMA does not describe, named the way a diagnostic names it, in file order and without repeats.
    (package private) static String
    unpack(Path archive, Path dest)
    Returns whatever the unpacker said, for a diagnostic; success is judged separately.
    (package private) static String
     
    (package private) static void
    unzip(Path archive, Path dest)
     
    (package private) static void
    Rewrites the invariant wrapper files from the running stage 0, leaving the project's compiler lock untouched.
    (package private) static void
    Moves this project to the newest published flixw.
    (package private) static void
    Validated on every run, not only when a download happens: a warm cache would otherwise hide a malformed mirror setting until the day it is actually needed.
    (package private) static void
    A pin is a dotted number and nothing else: no ranges, no `latest`, no vendor.
    (package private) static void
    validateUrl(String url, String where)
    Structural validation, so a malformed lock produces a FLIXW diagnostic rather than an uncaught IllegalArgumentException from URI.create deep in the download path.
    (package private) static String
     
    (package private) static String
    verbIdentity(Path jar, flixw.Lock lock, boolean override)
    Pinned compilers are identified by their locked digest; overrides by path+size+mtime.
    (package private) static List<String>
    verbs(Path javaExe, Path jar, String identity)
     
    (package private) static Path
    verbsFile(Path jar, String identity)
    Verb records live in the wrapper cache keyed by identity, never beside the JAR: a content-addressed compiler directory is legitimately read-only, and a FLIX_JAR override points at a JAR flixw does not own and must not write next to.
    (package private) static Path
    versionFile(String identity)
    Beside the verb record and keyed the same way, so a re-pin gets a fresh one.
    (package private) static flixw.Fail
     
    (package private) static flixw.Fail
     
    (package private) static flixw.Fail
     
    (package private) static flixw.Fail
     
    (package private) static flixw.Fail
     
    (package private) static flixw.Fail
     
    (package private) static flixw.Fail
     
    (package private) static flixw.Fail
     
    (package private) static flixw.Fail
     
    (package private) static void
    FLIXW010 and FLIXW011 are advisory: they are printed, they never set exit status.
    (package private) static void
     
    (package private) static void
    A pin naming a Java this machine does not have is written, and said out loud.
    (package private) static String
    IOException.getMessage() is often bare the path; name the failure too.
    (package private) static Path
    Resolves this file's own symlink chain without physicalizing unrelated directories.
    (package private) static void
     
    (package private) static void
    flixw's own namespace.
    (package private) static String
     
    (package private) static void
    wrapperVerb(String verb, List<String> rest, Path root, flixw.Lock lock, Path jar, flixw.Jvm jvm, List<String> compilerVerbs)
     
    (package private) static void
    writeAtomic(Path file, String text)
    Same-directory temp plus an atomic move.
    (package private) static void
    Written once, then never touched again -- unlike every other file install writes.
    (package private) static void
     
    (package private) static void
    writeVersionRecord(String identity, String reported)
    A blank record is written when the header carried no version, so it is asked once.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • WRAPPER_VERSION

      static final String WRAPPER_VERSION
      See Also:
    • WRAPPER_DIR

      static final String WRAPPER_DIR
      See Also:
    • MIN_JAVA

      static final int MIN_JAVA
      See Also:
    • SOURCE_FLOOR

      static final int SOURCE_FLOOR
      The oldest javac that can compile this file, which is a different number from the floor above and answers a different question. MIN_JAVA is what the *compiler* needs; this is what *stage 0* needs, and between the two lies the range where flixw runs, says the pinned Flix will not, and can fetch a JDK that will. Below it flixw cannot speak at all -- which is why the no-java diagnostic does not offer to install one. `tests/lint.sh` compiles this file with --release SOURCE_FLOOR so the number cannot quietly drift when a newer language feature is used.
      See Also:
    • TESTED_CEILING

      static final int TESTED_CEILING
      The interval flixw is tested on. Above the ceiling is a warning, not an error. The number means the suite has actually been run there, so it moves when that is done and not when a JDK is released: `.github/workflows/ci.yaml` runs the whole suite on the ceiling as well as on MIN_JAVA, which is what keeps the claim true rather than aspirational.
      See Also:
    • PROBE_TIMEOUT

      static final Duration PROBE_TIMEOUT
      Bounds for the two child processes stage 0 runs for information rather than for work. Both are generous: exceeding one means the child is wedged, not slow.
    • HELP_TIMEOUT

      static final Duration HELP_TIMEOUT
    • HELP_CAP

      static final int HELP_CAP
      See Also:
    • WRAPPER_VERBS

      static final List<String> WRAPPER_VERBS
    • BUILTIN_VERBS

      static final List<String> BUILTIN_VERBS
      Fallback verb set, observed in Flix 0.75.1 and 0.75.2. Used when `flix --help` cannot be captured or parsed. Its only job is to answer "does the pinned compiler already implement one of WRAPPER_VERBS" -- a question whose answer changes at most once a year, and never silently. Being one release stale here costs nothing; failing here would brick every project pinned to a compiler flixw has not seen.
    • T0

      static long T0
    • SEMVERISH

      static final Pattern SEMVERISH
    • LOCK_SCHEMA_VERSION

      static final String LOCK_SCHEMA_VERSION
      The lock format's major version, which is not the wrapper's. It changes only when a lock this stage 0 writes would stop being readable under the rules below; adding an optional key is not such a change, and does not move it.
      See Also:
    • PAGES_BASE

      static final String PAGES_BASE
      Where the generated documentation and the JSON Schema are published.
      See Also:
    • LOCK_SCHEMA_URL

      static final String LOCK_SCHEMA_URL
      The URL written into every generated lock as a `#:schema` directive, and the `$id` of the schema itself. Taplo and Even Better TOML read that directive, so an editor validates the lock with no per-project configuration.
      See Also:
    • REPO_PATTERN

      static final String REPO_PATTERN
      GitHub's own limits on the two path segments; a fork may live anywhere within them.
      See Also:
    • JAVA_PIN_PATTERN

      static final String JAVA_PIN_PATTERN
      A feature release or an exact one, and nothing else -- no ranges, no vendor.
      See Also:
    • LOCK_SCHEMA

      static final List<flixw.LockField> LOCK_SCHEMA
      Every key a lock may hold, in the order a generated lock writes them. required means required when the table it sits in is present, which is why `[java] version` is optional: a project that does not care which JDK runs the compiler omits the table entirely, and an empty one means the same thing.
    • NOTED_LOCKS

      static final Set<String> NOTED_LOCKS
      Locks already reported on, so a second read in the same run stays quiet.
    • UPSTREAM_REPO

      static final String UPSTREAM_REPO
      Where the stock compiler comes from when nothing says otherwise.
      See Also:
    • PIN_USAGE

      static final String PIN_USAGE
      One usage line for `pin`, because four diagnostics quote it and the fourth was already a release behind the first the last time one was written out by hand.
      See Also:
    • METADATA_CAP

      static final int METADATA_CAP
      Adoptium answers in a few tens of KiB; this is room to spare, not a target.
      See Also:
    • ADOPTIUM_API

      static final String ADOPTIUM_API
      See Also:
    • ADOPTIUM_RELEASES

      static final String ADOPTIUM_RELEASES
      See Also:
    • UNSAFE

      static final Pattern UNSAFE
    • VERSION_TOKEN

      static final Pattern VERSION_TOKEN
      A version token standing on its own, rather than one buried inside a longer word. Built from SEMVERISH so the two cannot drift into disagreeing about what a version is.
    • HEADER_LINES

      static final int HEADER_LINES
      How much of a help screen counts as the header for parseReportedVersion(java.lang.String).
      See Also:
    • COMMAND_ENTRY

      static final Pattern COMMAND_ENTRY
      Two-space indent, a lowercase name, then the column gap before its description.
    • SHIM

      static final String SHIM
      See Also:
    • CMD

      static final String CMD
      See Also:
    • SHIPPED

      static final List<String> SHIPPED
    • LOCAL_IGNORE

      static final String LOCAL_IGNORE
      `.flixw/local/` holds what only this machine knows -- currently the resolved JDK -- and must not be committed. The ignore rule lives inside the directory flixw owns, so adopting the wrapper does not edit a file the project maintains.
      See Also:
    • ENVRC_EXAMPLE

      static final String ENVRC_EXAMPLE
      A template for the one supported way to run a compiler flixw did not download. `FLIX_JAR` has always worked, and was findable only by reading one table row in docs/CONTRACT.md -- so in practice the people who needed it did not know it existed. A file sitting in the project says so without being read. The name is `.envrc.example`, not `.envrc`, and that is the whole point of the file rather than a detail of it. direnv refuses an `.envrc` it has not been shown, and reprints `direnv: error ... is blocked` on every cd into the directory until someone runs `direnv allow` or deletes it. The refusal is keyed on the file's hash, so a fully commented-out `.envrc` is blocked exactly like a live one: shipping one would hand recurring noise to the only population it could help. `.example` is inert.
      See Also:
    • FLIXW_LATEST

      static final String FLIXW_LATEST
      flixw's own releases. `latest/download` resolves without asking an API anything.
      See Also:
    • COMPLETION_SHELLS

      static final List<String> COMPLETION_SHELLS
    • COMPL_BASH

      static final String COMPL_BASH
      See Also:
    • COMPL_ZSH

      static final String COMPL_ZSH
      See Also:
    • COMPL_FISH

      static final String COMPL_FISH
      See Also:
    • COMPL_PWSH

      static final String COMPL_PWSH
      See Also:
    • VERBS_NOTE

      static final String VERBS_NOTE
      The name of the note stage 0 leaves for a completer, holding the verbs this project would actually dispatch. It lives beside local/java and is machine-specific for the same reason: it describes a resolved compiler, not the project.
      See Also:
    • COMPL_NOTE

      static final String COMPL_NOTE
      The note naming the pinned compiler's own completion script, when it ships one.
      See Also:
  • Constructor Details

    • flixw

      public flixw()
  • Method Details

    • fail

      static flixw.Fail fail(String code, int exit, String msg)
    • w001

      static flixw.Fail w001(String m)
    • w002

      static flixw.Fail w002(String m)
    • w003

      static flixw.Fail w003(String m)
    • w004

      static flixw.Fail w004(String m)
    • w005

      static flixw.Fail w005(String m)
    • w006

      static flixw.Fail w006(String m)
    • w007

      static flixw.Fail w007(String m)
    • w008

      static flixw.Fail w008(String m)
    • w009

      static flixw.Fail w009(String m)
    • w010

      static void w010(String m)
      FLIXW010 and FLIXW011 are advisory: they are printed, they never set exit status.
    • w011

      static void w011(String m)
    • env

      static String env(String k)
    • trace

      static boolean trace()
    • tr

      static void tr(String s)
    • validateVersion

      static String validateVersion(String v, String where)
    • stripTagPrefix

      static String stripTagPrefix(String v)
      Accepts the release tag where a version is expected: v0.75.2 means 0.75.2. GitHub shows the tag, not the version. The releases page, the tag list, the archive links and the asset URLs all read v0.75.2, so copying from where the versions actually are gets you the tag every time -- and flixw itself builds "v" + version to construct that URL, so it already holds that the two name one release. Refusing the form flixw prints into its own URLs made the user do a normalization the wrapper was doing anyway. Only ahead of a digit, so vNext is still a bad version rather than the version Next, and the diagnostic keeps naming the real problem. Deliberately not applied to [package].flix: that field is Flix's, and Flix accepts x.x.x alone. Tolerating a tag there would let flixw read a manifest that Flix itself rejects, which is a worse outcome than the error it replaces.
    • canonical

      static String canonical(String v)
      The single normalization used for release tags, cache coordinates, and every version comparison. SemVer build metadata identifies a build, not a release, so it is accepted in the manifest and stripped everywhere it would name an artifact. Defining this once is what stops `flix = "0.75.2+build.4"` from producing a drift error that `./flixw pin` cannot repair.
    • triple

      static String triple(String v)
      The `x.x.x` that `[package].flix` is allowed to hold. That field is Flix's, not flixw's, and Flix rejects anything else outright -- "This toml file has a Flix version number of the wrong length" for a version carrying build metadata. It also accepts 99.99.99 against a 0.75.2 compiler, so it reads as a coarse floor rather than a pin. The exact version therefore lives in the lock, which is flixw's own file and can say `0.75.2+fork.wstein.260807.1` without breaking anything. Drift compares the two at this precision, because that is all the manifest is able to express.
    • q

      static String q(String s)
    • why

      static String why(Exception e)
      IOException.getMessage() is often bare the path; name the failure too.
    • redact

      static String redact(String v)
      Redacts credentials from a URL-shaped value before it is printed. `doctor` output exists to be pasted into bug reports, and a proxy URL is the one environment value that routinely carries a password. Host and port are what a reader needs; user-info and query string never are. Values that are not URLs at all -- a NO_PROXY host list, say -- have no '@' and pass through untouched.
    • redactOpts

      static String redactOpts(String v)
      The same, for JVM option strings, which can carry -Dhttps.proxyPassword=secret.
    • lockTables

      static List<String> lockTables()
      The tables the schema knows about, deduplicated, in lock order. The root is "".
    • lockSchemaJson

      static String lockSchemaJson()
      The published JSON Schema for lock.toml, rendered from LOCK_SCHEMA. Generated rather than hand-written for the reason the shims are compared byte for byte: a schema describing a lock this wrapper no longer writes is worse than no schema at all, because an editor presents it as authority. `tests/lint.sh` diffs this against the copy in `docs/schema/`, so the published file cannot drift from the code that writes the file it describes. Hand-rolled rather than serialised by a library, because stage 0 has no dependencies. The only values interpolated are ours, and jsonString(java.lang.String) escapes them anyway -- the patterns are full of backslashes.
    • lockFields

      static List<flixw.LockField> lockFields(String table)
      The fields declared for one table, in lock order.
    • fieldJson

      static String fieldJson(flixw.LockField f, String indent)
    • tableJson

      static String tableJson(String table, String indent)
    • jsonArray

      static String jsonArray(List<String> items)
    • jsonString

      static String jsonString(String s)
      JSON string literal. Only the escapes RFC 8259 requires; every value here is ASCII.
    • tomlScan

      static flixw.TomlScan tomlScan(String text, String where)
      The single TOML line scanner in stage 0. This is not a TOML parser and does not try to be one -- stage 0 has no dependencies by design. It is deliberately table-aware, comment-aware and multi-line-string-aware, because the alternative that a plain regex gives you is reading `flix = "..."` out of some unrelated table, or out of the body of a description string. There is exactly one of these because there used to be two: `pin`'s rewrite carried a second copy that had never learned about multi-line strings, so a `flix = "9.9.9"` inside a `"""` description was correctly invisible to the lookup and yet rewritable by pin. Any divergence here means the version flixw reads is not the one it writes, so the two readers share a scanner rather than a convention. Lines are split on \n alone, never on \r?\n: `pin` rejoins with \n to rewrite a single line in place, and a split that swallowed the \r would quietly convert a CRLF manifest to LF. The trailing \r survives into the raw line and is removed by trim().
    • isKey

      static boolean isKey(flixw.TomlEntry e, String table, String key)
      True when an entry is `table.key`. Dotted keys are resolved to their table by tomlScan(java.lang.String, java.lang.String), so both spellings arrive here already in the same shape.
    • splitKey

      static List<String> splitKey(String raw, String where)
      Splits a key into its segments, respecting quotes, then unquotes and trims each one. `a.b` is two segments; `"a.b"` is one. Fails closed: an unterminated quote or an empty segment is a manifest this scanner will not guess at.
    • tomlLookup

      static String tomlLookup(String text, String table, String key, String where)
      Reads one key from one TOML table. Anything it cannot classify inside the table it was asked about is rejected rather than guessed at. Duplicate tables and duplicate keys are ambiguous, so they fail rather than resolve. Accepts the key inside [table] and as a dotted key at the root (`package.flix`).
    • bracketDelta

      static int bracketDelta(String line)
      How much this line opens or closes an inline array, counting only brackets outside quotes. Used to skip a value that spans lines; it never goes below zero, because a stray closing bracket is not this scanner's business to diagnose.
    • stripComment

      static String stripComment(String line)
      Strips a trailing comment, ignoring '#' inside quotes.
    • unquote

      static String unquote(String s)
    • lockPath

      static Path lockPath(Path root)
    • readLock

      static flixw.Lock readLock(Path lockFile)
    • readLockFields

      static Map<String,String> readLockFields(String text, String where)
      Reads every key LOCK_SCHEMA declares, keyed as `table.key` with the root table's keys unprefixed. Absent optional keys are simply not in the map. Presence and shape are both checked here, from the same list the published JSON Schema is rendered from, so a lock an editor flags is a lock flixw refuses -- and the diagnostic can say what the key is *for* rather than quoting a regex at someone.
    • noteUnknownLockKeys

      static void noteUnknownLockKeys(String text, String where, String wroteIt)
      Keys the schema does not describe, reported once and never fatally. Advisory because the ordinary way to meet one is a lock written by a newer flixw, and refusing to run would make such a project unbuildable by every collaborator who had not upgraded yet -- the lock is committed, so that is most of them. Silence is the wrong answer too: a mistyped key is otherwise invisible, and the value someone believed they had set is simply never read. A lock that says it was written by a newer flixw gets no note at all, because there the unknown key is expected and the message would be wrong as well as noisy.
    • unknownLockKeys

      static List<String> unknownLockKeys(String text, String where)
      Every key in the file that LOCK_SCHEMA does not describe, named the way a diagnostic names it, in file order and without repeats. Separate from the note because `doctor --fix` asks the same question for the opposite reason: it regenerates the lock from the values it read, which would *delete* any key it did not read.
    • manifestVersion

      static String manifestVersion(Path manifest)
      The manifest is the human authority; disagreement stops us before the network. A manifest that exists but cannot be read is an error, not an absent declaration -- swallowing it would silently disable drift detection and let the compiler run.
    • isWindows

      static boolean isWindows()
    • isMac

      static boolean isMac()
    • cacheHome

      static Path cacheHome()
    • sha256

      static String sha256(Path file)
    • sha256

      static String sha256(byte[] b)
    • checkRepo

      static String checkRepo(String repo, String where)
    • encodeTag

      static String encodeTag(String tag)
      A release tag in a URL path. Only '+' needs it; the rest of a version is path-safe.
    • resolveRelease

      static flixw.Asset resolveRelease(String repo, String version)
      Resolves the compiler artifact for one repository and version, without asking an API anything. The GitHub API answered this in one call and threw in a digest, and it was the wrong tool: unauthenticated it allows sixty requests an hour across everything on the machine, so `pin` failed with HTTP 403 for a tag that plainly existed, and the error blamed the tag. Release *downloads* carry no such limit, so the asset name -- the only thing that was ever unknown -- is found by asking for the file itself. Upstream is a single constructed URL, as before. A fork is probed against the two conventions in the wild, flix-<version>.jar and `flix.jar`, with a HEAD each; the download that follows is still exactly one acquisition attempt for one artifact.
    • httpClient

      static HttpClient httpClient()
      The one HTTP client, pinned to HTTP/1.1. Every request flixw makes is a single one-shot HEAD or GET, so HTTP/2 buys nothing here -- there are no concurrent streams to multiplex onto one connection -- and it costs a failure mode that only shows up as a red CI run. When a server sends GOAWAY while a stream is being opened, the JDK client raises `request not processed by peer`; because acquisition is one attempt with no retry loop, that lands on the user as a failed download and, through the missing lock, as fifteen further failures. That is a real observation, not a theoretical one: it took out the whole windows smoke job on ccba32b while ubuntu and macos passed the same commit. Pinning 1.1 deletes the race rather than retrying around it, which is the trade this project already makes everywhere else -- a retry would have to be bounded, logged and explained, and would still leave the request that *was* processed ambiguous.
    • assetExists

      static boolean assetExists(String url)
      Does this release asset exist? A HEAD, so the download itself stays a single attempt.
    • parsePin

      static flixw.Pin parsePin(List<String> args, flixw.Lock existing)
      ./flixw pin [<owner>/<repo>] [<version>] [--java <version>], or ./flixw pin --refresh. The two are told apart by the slash, which a version can never contain -- the grammar rejects it -- so the order does not matter and neither does a flag. An omitted repository means the one already in the lock, so re-pinning a project that tracks a fork stays on that fork: rebuilding the upstream URL every time silently moved such a project back to stock, and because both are honestly version 0.75.2, nothing about it looked wrong.
    • insideCompilerCache

      static boolean insideCompilerCache(Path jar)
      True when a path names something inside the cache flixw fills with pinned compilers.
    • reportOverrideGap

      static void reportOverrideGap(flixw.Lock lock, Path jar)
      Says so when FLIX_JAR names a compiler out of flixw's own cache. A mismatch between the override and the lock is the *ordinary* case -- the override exists to run a jar you built yourself, which is not the pinned one and is not meant to be -- so it is reported where state is printed, and not on every run. Pointing it inside <cache>/compilers/ is different, and is always a mistake. Those names are content-addressed, flix-<version>-<sha256>.jar, so **the path changes every time the project is re-pinned**. An override set once to whatever `info` reported that day goes on naming the superseded artifact afterwards, and the project quietly builds with the compiler it used to pin. Nothing else in flixw could catch it: the digest guard is switched off by the override, and the version check passes because two builds of one release share a canonical version. Matching the lock is a mistake too, only a harmless one: it names the jar flixw would have chosen anyway, and it will stop doing that at the next pin.
    • compilerPath

      static Path compilerPath(flixw.Lock lock)
    • validateDistUrl

      static void validateDistUrl()
      Validated on every run, not only when a download happens: a warm cache would otherwise hide a malformed mirror setting until the day it is actually needed.
    • validateUrl

      static void validateUrl(String url, String where)
      Structural validation, so a malformed lock produces a FLIXW diagnostic rather than an uncaught IllegalArgumentException from URI.create deep in the download path.
    • rewriteBase

      static String rewriteBase(String url)
    • acquire

      static Path acquire(flixw.Lock lock)
    • download

      static void download(String url, Path dest)
    • runCapture

      static String runCapture(List<String> cmd, Duration timeout, int cap) throws IOException
      Runs a child and returns its merged output, bounded in both bytes and wall clock. Returns null when the child did not finish in time, or its output could not be read. The obvious shape -- a read loop with a deadline test in its condition -- bounds nothing: the test runs *between* reads, and read() on a pipe blocks until the writer produces a byte or closes it. A child that starts and then answers nothing parks stage 0 inside that one call forever, which is precisely what a process run for information must never do. So the read runs on a daemon thread and the timeout is enforced on the process, which is the only handle that can actually be revoked. The byte cap is a separate bound: a chatty child would otherwise exhaust the heap.
      Throws:
      IOException
    • exeIn

      static Path exeIn(String home)
    • probe

      static int probe(Path exe)
      Reads <home>/release when present and parseable; else runs the candidate once.
    • probeVersion

      static String probeVersion(Path exe)
      The candidate's own version string -- `21.0.12` rather than `21` -- so a pin can be as exact as the person who wrote it chose to be. Same two sources as probe(), in the same order and for the same reasons: the release file costs one read, and executing the candidate is the fallback for a java that is not laid out like a JDK.
    • satisfiesJavaPin

      static boolean satisfiesJavaPin(String pin, String version)
      Does this JDK satisfy `[java] version` in the lock? A pin is a prefix of the version, cut at a dot: `21` accepts 21.0.12, `21.0` accepts 21.0.12, and neither accepts 21.1 or 2. Prefix rather than equality because the useful pin is nearly always "this feature release", and the exact one is available to whoever wants it by writing more of the number. Vendor is deliberately not part of it -- the pin says which Java the project needs, not whose.
    • validateJavaPin

      static void validateJavaPin(String v, String where)
      A pin is a dotted number and nothing else: no ranges, no `latest`, no vendor. It must also be a Java the pinned compiler can actually run under, so a pin below MIN_JAVA is refused at the point it is written rather than at every run afterwards.
    • feature

      static Integer feature(String v)
    • strictJava

      static boolean strictJava()
    • acceptable

      static boolean acceptable(int f, String source)
      Below MIN_JAVA is always fatal: the compiler will not run. Above TESTED_CEILING is a warning, because a JDK upgrade must not break a wrapper whose pinned compiler tolerates it. FLIXW_STRICT_JAVA=1 makes the ceiling fatal for reproducible builds.
    • chooseInstall

      static flixw.Jvm chooseInstall(List<flixw.Jvm> candidates, boolean strict)
      Picks among discovered installations: the newest JDK that is still inside the tested interval, and only if none is, the one just above it. Taking the first acceptable candidate in directory order was the earlier rule, and it answers by filename. On a machine carrying 11, 17, 21, 25 and 26 it selected 26 -- outside the tested interval, and warned about on every run -- because the symlink named `java` sorts before `openjdk@21`. Nothing was wrong with the search; the choice was made by `sort`. Above the ceiling is a last resort rather than a preference, so the lowest such candidate wins: it is the one closest to ground that has actually been tested. Returns null when nothing is usable, which is the caller's cue to fail.
    • selectJava

      static flixw.Jvm selectJava(String pin)
    • knownInstalls

      static List<Path> knownInstalls()
      Directories a JDK is commonly unpacked into. Deliberately only directories: the OS-native inventories are either unusable or misleading here. `java_home -V` is blind to Homebrew, which on macOS is where the JDKs usually are; `update-alternatives --config` is interactive and wants root; `dpkg`, `rpm`, `scoop list` and `choco list` answer with package names rather than paths; and `find /` is an unbounded walk on a tool that runs on every command. A directory that is not there costs one stat.
    • jdkArch

      static String jdkArch()
      aarch64 or x64 as Adoptium spells it, or null where it publishes nothing for us.
    • jdkArchiveType

      static String jdkArchiveType()
      Windows gets a zip; nobody publishes a tar.gz for it.
    • httpGet

      static String httpGet(String url)
      One bounded HTTPS GET returning text. Metadata only; bytes go through download().
    • jsonField

      static String jsonField(String json, String key)
      Enough JSON for flat string fields of one small, known response.
    • jsonObject

      static String jsonObject(String json, String key)
      The first brace-balanced object under `"key":`. Enough for this one response, whose values are URLs, digests and filenames and contain no braces of their own.
    • resolveTemurin

      static flixw.JdkPackage resolveTemurin(int feature)
      Resolves the current Temurin release for this platform. The response describes an `installer` -- a .pkg or .msi -- *before* the `package` that is the archive, and both carry a `checksum` and a `link`. Reading the first match in the document would fetch a macOS installer package and verify it against its own digest: consistently, and uselessly. The fields are read out of the `package` object for that reason.
    • installJdk

      static Path installJdk(flixw.JdkPackage p)
      Downloads, verifies and unpacks one JDK into the wrapper cache, and returns its `java`. The directory is named for the archive, which carries the exact build, so a second project on the same machine reuses it and a re-run is a no-op.
    • installedJdk

      static Path installedJdk()
      The java recorded by the last successful install, if it is still there.
    • unpack

      static String unpack(Path archive, Path dest) throws IOException
      Returns whatever the unpacker said, for a diagnostic; success is judged separately.
      Throws:
      IOException
    • unzip

      static void unzip(Path archive, Path dest) throws IOException
      Throws:
      IOException
    • findJavaUnder

      static Path findJavaUnder(Path root)
      Layout differs per platform -- macOS nests a .jdk bundle -- so look rather than guess. The executable bit is only required where it means something. Adoptium builds its Windows zip on a Unix machine, so entries carry a mode of 0770, and java.util.zip discards it: every file lands 0644. On Windows that is irrelevant, because what makes java.exe runnable there is the extension and the ACL -- but a check for it would rest on platform semantics rather than on anything unpacking guarantees. On POSIX the bit does mean something and tar preserves it, so it is still required.
    • jdkInstructions

      static void jdkInstructions(int want)
      What to type on this OS, pointing at the same vendor flixw would fetch.
    • offerJdk

      static boolean offerJdk(int want)
      Offers to fetch one only when there is somebody to answer. A prompt written into a pipe, a CI log or a hook is not a question, it is a hang, so those get the instructions and a failure instead -- and an opt-in they can set once.
    • javaPinAvailable

      static boolean javaPinAvailable(String pin)
      Is there a JDK on this machine that satisfies the pin? Asked by `pin` so that writing one is not silently a decision to break the next command. It never offers, downloads or throws: the answer is used for a note, and a pin for a JDK this machine does not have is legitimate -- CI may have it, and `--install-jdk` can fetch it.
    • noJavaFound

      static flixw.Jvm noJavaFound(int self, String pin)
      Nothing usable was found: say how to fix it, then offer to do it.
    • installJdkVerb

      static void installJdkVerb(List<String> argv)
      `./flixw wrapper --install-jdk`, so the choice need not wait for a failure.
    • jvmOpts

      static List<String> jvmOpts()
    • tokenize

      static List<String> tokenize(String s)
      One documented tokenizer: whitespace separates; '' and "" quote; \ escapes inside "" and bare.
    • wrapperAnchor

      static Path wrapperAnchor()
      Resolves this file's own symlink chain without physicalizing unrelated directories.
    • resolveLinkChain

      static Path resolveLinkChain(Path p) throws IOException
      Throws:
      IOException
    • findRoot

      static Path findRoot(Path anchor)
      Search upward from cwd for flix.toml, bounded above by the wrapper's own project. Invocation from outside that tree is refused rather than searched: an unbounded walk finds the first stray manifest above cwd and silently builds an unrelated project.
    • verbsFile

      static Path verbsFile(Path jar, String identity)
      Verb records live in the wrapper cache keyed by identity, never beside the JAR: a content-addressed compiler directory is legitimately read-only, and a FLIX_JAR override points at a JAR flixw does not own and must not write next to.
    • verbIdentity

      static String verbIdentity(Path jar, flixw.Lock lock, boolean override)
      Pinned compilers are identified by their locked digest; overrides by path+size+mtime.
    • verbs

      static List<String> verbs(Path javaExe, Path jar, String identity)
    • versionFile

      static Path versionFile(String identity)
      Beside the verb record and keyed the same way, so a re-pin gets a fresh one.
    • cachedVersionRecord

      static String cachedVersionRecord(String identity)
      The version record verbs(java.nio.file.Path, java.nio.file.Path, java.lang.String) already wrote, if any -- a file read, never a subprocess. Used to list cached compilers by what they actually report ( 0.75.3+stable.names.4, not just the canonical 0.75.3 the cache directory names), without paying for a `--help` capture per cached jar on a verb that stays offline and fast.
    • writeVersionRecord

      static void writeVersionRecord(String identity, String reported)
      A blank record is written when the header carried no version, so it is asked once.
    • reportedVersion

      static String reportedVersion(Path javaExe, Path jar, String identity)
      What the pinned compiler says its version is, or null if it will not say. Read from the cache written when the verbs were captured. A cache filled by an earlier flixw has the verbs and not this, so it is captured once and kept -- one subprocess, on one run, rather than leaving every project upgraded from an older release permanently unchecked.
    • reportVersionGap

      static void reportVersionGap(flixw.Lock lock, String reported)
      Says so when the compiler is not the version the lock claims. The digest settles *which bytes* run and nothing settles that those bytes are the release the lock names. A mislabelled release asset -- a fork that tagged v0.75.4 over a 0.75.2 build, an upstream re-upload -- is pinned, verified and run without a word, and info goes on reporting the lock's version forever. The compiler's own answer is the only second opinion available, and it is already on screen when the verbs are captured. Compared through canonical(java.lang.String), because build metadata identifies a build rather than a release: a compiler built from 0.75.3+stable.names.3 reporting 0.75.3 is agreeing, not disagreeing, and warning on every run of every fork would train the reader to ignore the line that matters. That difference is still visible -- info and doctor print both strings whenever they differ. FLIXW010: printed, never fatal. The compiler is the authority on what it will run, this is flixw's account of what was asked for, and a wrapper that refused to start over a version string would be wrong more often than the mismatch is.
    • captureHelp

      static String captureHelp(Path javaExe, Path jar)
      The compiler's `--help`, bounded, as text. The two parses read it separately.
    • captureVerbs

      static List<String> captureVerbs(String out, Path jar)
    • parseReportedVersion

      static String parseReportedVersion(String help)
      The version the compiler says it is, read from the header of its own help. Free, because verbs(java.nio.file.Path, java.nio.file.Path, java.lang.String) already runs --help and the version is sitting in the text it throws away. Both renderers put it in the first lines and neither puts it in the same place: scopt writes The Flix Programming Language 0.75.2 on one line, picocli writes the product name and the version on the next. Rather than encode either layout -- a fork may rename the product string, and one did move the version to its own line -- take the first standalone version token in the header. The header, not the whole screen: an option's default or an example further down is text about something else, and reading one as the compiler's identity would produce a mismatch report about nothing.
      Returns:
      the reported version, or null when the header carries none -- which is not an error, only the absence of a second opinion
    • parseVerbs

      static List<String> parseVerbs(String out)
      The verbs a help screen advertises, by three independent parses. Two help renderers are in play and neither is a contract. Stock Flix is scopt: the verb list is one `Usage: flix [a|b|c]` line, and each verb repeats as `Command: a`. The picocli-based fork wraps that same bracket across several lines and replaces the per-verb lines with one indented `Commands:` block. A parser that handles only the first reports zero candidates on the second, which is what FLIXW010 was saying. Kept separate from the subprocess that produces the text so it can be tested against both renderers' real output without a JAR; `tests/UnitCheck.java` does exactly that.
    • stage0Dir

      static Path stage0Dir(String srcHash)
    • selfCompile

      static void selfCompile(Path source)
      Compiles this source into the cache so the shim can skip the JEP 330 source launch next time. The shim -- not stage 0 -- consults this cache: stage 0 is already the running process by the time it could decide. That is the one place the shim is allowed to know a cache layout, and it is why the cache path is a versioned interface between the shim and stage 0.
    • deleteTree

      static void deleteTree(Path p)
    • wrapperVerb

      static void wrapperVerb(String verb, List<String> rest, Path root, flixw.Lock lock, Path jar, flixw.Jvm jvm, List<String> compilerVerbs)
    • askedVersion

      static String askedVersion(flixw.Lock lock, Path jar, flixw.Jvm jvm)
      The reported version for the verbs that print state; null whenever there is no jar.
    • report

      static void report(Path root, flixw.Lock lock, Path jar, flixw.Jvm jvm, List<String> cv, String reported)
    • listCache

      static void listCache(flixw.Lock lock, flixw.Jvm jvm)
      Everything already sitting on this machine -- compilers and JDKs flixw itself cached, plus the JDKs knownInstalls() can already see without a network call. Not what could be pinned or provisioned: that would mean asking a remote API on a verb the paper promises stays offline. `info` reports state; a catalogue of upstream releases is a different feature with a different cost, and does not belong behind the same flag.
    • humanSize

      static String humanSize(long bytes)
      A byte count as a person reads it; cached JARs and JDKs are always well above 1 KB.
    • checkCanonical

      static int checkCanonical(Path file, String canonical, String label)
      Compares a committed invariant file against the bytes this wrapper release ships.
    • canonicalAttrs

      static String canonicalAttrs(String shipped)
      The line endings the flixw block pins for one shipped path.
    • patternMatches

      static boolean patternMatches(String pattern, String path)
      Does one .gitattributes pattern match one path flixw ships?
    • checkGitattributes

      static int checkGitattributes(Path ga)
      gitattributes resolves by *last* matching pattern, so a rule after the wrapper block silently overrides it -- and a checked-out shim with the wrong line endings is exactly the failure the block exists to prevent. What counts as an override is the resulting attribute, not the mere presence of a later rule: a repetition of what the block already says changes nothing, and calling it harmful would send someone hunting for a problem they do not have.
    • count

      static int count(String haystack, String needle)
    • git

      static Integer git(Path root, String... args)
      Runs git <args> in root; null when git is absent or the command fails to start.
    • check

      static int check(Path root, flixw.Lock lock, Path jar, flixw.Jvm jvm)
      Every check, printed; the count is the caller's to act on.
    • writeAtomic

      static void writeAtomic(Path file, String text) throws IOException
      Same-directory temp plus an atomic move. A direct write is not a single event: a termination or a power loss in the middle of one leaves a truncated manifest or a half-written lock, and the rollback that was supposed to cover that cannot run either. Renaming a complete file over the old one has no such window.
      Throws:
      IOException
    • restore

      static void restore(Path file, String previous)
      Best-effort rollback; a failed restore must not mask the failure being reported.
    • pin

      static void pin(Path root, flixw.Pin what)
    • warnMissingJava

      static void warnMissingJava(String javaPin)
      A pin naming a Java this machine does not have is written, and said out loud. It is not an error -- the machine that runs the build may not be this one -- but finding out at the next command, from a diagnostic about a missing JDK, is finding out late.
    • lockText

      static String lockText(String wrapper, String repo, String version, String url, String sha256, String java)
      One place that knows what a lock looks like, so the writer cannot drift by table. The first line is a Taplo `#:schema` directive, which Even Better TOML and taplo both honour: an editor validates the lock against the published schema with nothing configured per project, which is the only way a generated file gets checked by the person editing it by hand against the advice at the top of it. It names the versioned schema rather than a floating one, for the reason the compiler pin names an exact version -- a lock is a pin, including of what it means.
    • install

      static void install(Path target, Path source)
    • writeLocalIgnore

      static void writeLocalIgnore(Path target) throws IOException
      Throws:
      IOException
    • writeEnvrcExample

      static void writeEnvrcExample(Path target) throws IOException
      Written once, then never touched again -- unlike every other file install writes. The others are flixw's: they are executed or parsed, drift in them breaks a run, and `doctor --fix` restoring them is a repair. This one sits at the project root among files the project owns, nothing reads it, and its whole purpose is to be copied and edited. Rewriting it on drift would be overwriting someone's notes to restore a file that does nothing. For the same reason it is absent from SHIPPED, from doctor's canonical comparison and tracked-file audit, and from the .gitattributes block: deleting it is a valid answer, and nothing should nag about that.
      Throws:
      IOException
    • migrateFromFlixNames

      static void migrateFromFlixNames(Path target) throws IOException
      Moves a project installed under the pre-0.20 names onto the current ones. Until 0.20 the wrapper shipped as `flix`, `flix.cmd` and `.flix-wrapper/flix.java`, which read as the compiler's own name on a tool that is not the compiler. Installing over such a project would otherwise leave both sets side by side, and the pin -- the one file here that is the project's rather than ours -- would still be in the old directory, where nothing reads it. So the lock moves first, and the old files are removed only when they are recognisably the ones flixw wrote: a shim someone edited, or a directory holding anything else, is left alone and reported.
      Throws:
      IOException
    • updateWrapper

      static void updateWrapper(Path root)
      Rewrites the invariant wrapper files from the running stage 0, leaving the project's compiler lock untouched. This repairs the failures that actually happen: a shim that lost its executable bit to an archive download, a hand-edited shim, a .gitattributes block clobbered by a merge. It deliberately does not fetch a newer flixw. Self-update needs a published release feed with its own digests, which does not exist; until it does, upgrading means running `install` from the newer release, and saying so is better than pretending.
    • refreshLock

      static flixw.Refresh refreshLock(Path root) throws IOException
      Throws:
      IOException
    • refreshPin

      static void refreshPin(Path root)
      `./flixw pin --refresh`. Offline: the compiler is not re-resolved, not re-downloaded and not re-hashed, and the pin does not move. What changes is the file's shape -- the `#:schema` line a lock written before it existed does not carry, the recorded wrapper version, the layout -- which is why it is a form of `pin` and not of `upgrade`.
    • mergeGitattributes

      static void mergeGitattributes(Path ga) throws IOException
      Throws:
      IOException
    • olderOrSame

      static boolean olderOrSame(String a, String b)
      Is `a` no newer than `b`? Both are the wrapper's own dotted versions.
    • num

      static int num(String s)
    • upgradeWrapper

      static void upgradeWrapper(Path root)
      Moves this project to the newest published flixw. The old `--upgrade` rewrote the files from the stage 0 already in the tree, which is a repair rather than a version change -- so it printed a note on every run explaining that it had not done what its name says. That repair is now `./flixw doctor --fix`, and this does what the word means. The new stage 0 installs itself. It is the only thing that knows its own shim bytes, and having the old one write files for a version it has never seen is how the two drift apart. The digest is checked against the SHA256SUMS published beside it -- same origin, same TLS, so this catches a corrupted or truncated download and not a compromised release. That is the same footing as the compiler pin, and docs/LIMITATIONS.md says so; a self-update is simply where it matters most.
    • wrapperNamespace

      static void wrapperNamespace(List<String> argv)
      flixw's own namespace. wrapperUsage(java.lang.String) is the one list of what it offers. One verb, and every flixw-only operation under it as a flag. These are not stand-ins for anything Flix might one day ship, so they neither retire nor compete for a name with something that will: `pin`, `info`, `doctor` and `validate` deliberately collide with names Flix could claim, and step aside the day it does. Rewriting flixw's own files, or reporting flixw's own version, never will. Answered before the project, the lock, the network and the compiler, for the same reason the flags it replaces were: a bare verb is subject to compiler-first dispatch, and a compiler that happened to claim `wrapper` would make these unreachable at exactly the moment someone needs them to repair the installation. FLIX_BACKEND does not reach them either.
    • wrapperUsage

      static String wrapperUsage(String problem)
    • completionScript

      static String completionScript(String shell)
      A completion script for one shell, on stdout. The script is static and the data is not. Completion candidates depend on the pinned compiler -- compiler-first dispatch means a verb set that changes with the lock -- so a script that baked them in would go stale at the next pin and say nothing about it. Instead the script reads them at TAB time from .flixw/local/verbs, which stage 0 rewrites on every run that resolves a compiler. That also keeps the JVM out of the completion path: a TAB press costs a file read, not a stage 0 launch plus the mandatory digest re-hash, which together are slower than typing the verb. The verb list compiled into each script is the fallback for a project that has not resolved a compiler yet -- the same bargain BUILTIN_VERBS makes, and stale in the same harmless way.
      Parameters:
      shell - one of COMPLETION_SHELLS
      Returns:
      the script text, ending in a newline
    • recordVerbs

      static void recordVerbs(Path root, List<String> compilerVerbs)
      Records the verbs this project dispatches, for a completer to read. The union, not the compiler's set alone: a wrapper verb the compiler has claimed is still a verb the user can type, and one it has not claimed is still handled here. Which side runs it is dispatch's business and no help to someone pressing TAB. Every failure is discarded, exactly as in recordJava(java.nio.file.Path, java.nio.file.Path): a read-only checkout or a deleted directory is not worth a diagnostic for a note whose absence only costs a completer its per-project accuracy.
    • recordCompletion

      static void recordCompletion(Path root, Path script)
      Same, for the path to the compiler's own completion script; absent means none.
    • recordNote

      static void recordNote(Path root, String name, String body)
    • compilerCompletion

      static Path compilerCompletion(Path javaExe, Path jar, String identity, List<String> verbs)
      The pinned compiler's own completion script, cached, or null if it has none. Detection costs nothing and needs no version sniffing: picocli registers generate-completion as an ordinary subcommand, so it arrives in the verb set parseVerbs(java.lang.String) already captured. Stock Flix is scopt, never advertises it, and takes this path zero times -- which is the whole reason the check is a set membership rather than a probe. flixw does not read, rewrite or splice what comes back. The generated script's internal shape is picocli's business and changes with picocli; the one line flixw looks at, at completion time and in shell, is the complete -F registration every bash completion script must end with. Splicing was the alternative and it is worse than it looks: parseVerbs(java.lang.String) guessing wrong falls back to a verb table, while a bad splice puts broken bash in someone's shell startup. Cached beside the verb record and keyed the same way, so a re-pin gets a new one and an override never writes next to a JAR flixw does not own.
    • main

      public static void main(String[] args)
      The one entry point. Every failure inside is a flixw.Fail, which carries both the FLIXWnnn code printed on stderr and the advisory exit status; nothing else writes an exit status, so a code the user's own program returns cannot be confused with one of ours by accident of where it was thrown.
      Parameters:
      args - the wrapper's argv, passed on to the compiler unchanged when dispatch decides the compiler owns them
    • realMain

      static void realMain(List<String> argv)
    • routingNotice

      static void routingNotice(String verb, String compilerVersion)
      Which side handled a verb, under FLIXW_TRACE only. It used to print on every wrapper-handled command, and it told the caller what they had already said: typing `./flixw doctor` and being told that doctor went to the wrapper is not news. Worse, it read as a warning -- something had happened worth mentioning -- when nothing had. The hot path was already silent; now the rest is too, and the routing is still visible to anyone debugging it.
    • wrapperHelp

      static void wrapperHelp()
    • sourceLaunchPath

      static Path sourceLaunchPath()
      The .java file this stage 0 was launched from, or null when it is running as the compiled class out of the cache. A source launch knows its own path, and that knowledge outranks FLIXW_SOURCE. FLIXW_SOURCE is the shim telling the *compiled* stage 0 which source it was built from; it says nothing about a stage 0 launched by path. `wrapper --upgrade` hands its environment to a freshly downloaded stage 0 in a temporary directory, which is a different file in a different project-less place -- and believing the inherited variable there anchored the new wrapper in the old project, where a lock exists, so `install` was no longer first contact and went to the compiler: `Unrecognized file extension: 'install'.` Every upgrade failed that way.
    • selfSource

      static Path selfSource()
    • recordJava

      static void recordJava(Path root, Path exe)
      Leaves the shim a note saying which JDK this project resolved to, so the next run can start on it instead of starting on whatever `java` is first on PATH and then relaunching. Without it a project that pins a Java the machine does not use by default pays a whole extra stage 0 -- about 100ms -- on every command. Machine-specific, therefore not committed: `.flixw/.gitignore` keeps `local/` out of git, and flixw writes that file itself rather than editing the project's own .gitignore. It names an executable the shim will run, which sounds like a new trust boundary and is not one: anyone who can write `.flixw/local/` can edit `./flixw` itself, which is simpler and does more. Every failure here is discarded. A read-only checkout, a directory someone deleted, a race with another run -- none of it is worth a diagnostic for a cache whose only job is to save a process start, and whose absence is already the old behaviour.
    • relaunch

      static boolean relaunch(flixw.Jvm jvm, List<String> argv)
      At most one relaunch, guarded by an env marker, so a stale release file cannot loop.
    • awaitWithReaper

      static int awaitWithReaper(Process p) throws InterruptedException
      Waits for a child that owns the terminal, and guarantees it dies with us. Java has no exec(2): stage 0 must stay resident for the child's whole life. The child keeps the terminal, so SIGINT reaches it through the foreground process group. The hook covers the rest: without it, a SIGTERM to stage 0 orphans a compiler that then runs forever. SIGKILL still orphans it -- no Java code can prevent that, and the README says so. The relaunch path shares this. It used to wait bare, so terminating a stage 0 that had relaunched itself into another JVM orphaned the entire subtree beneath it -- the same defect the compiler launch had a hook for, one process further down.
      Throws:
      InterruptedException
    • launch

      static void launch(Path javaExe, List<String> opts, Path jar, List<String> args)
      Inherit cwd and the three streams; propagate the child's status.