Class flixw
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 ClassesModifier and TypeClassDescription(package private) static final recordOne 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 recordOne 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 recordWhat one `pin` command line asks for; `parsePin` is the only thing that builds it.(package private) static final recordWhether the lock was rewritten, and the sentence explaining why not when it was not.(package private) static final recordOne `key = value` occurrence, the table it was found in, and the line it sits on.(package private) static final recordEvery scalar entry in a document, plus every table header, in file order. -
Field Summary
FieldsModifier and TypeFieldDescription(package private) static final String(package private) static final StringFallback verb set, observed in Flix 0.75.1 and 0.75.2.(package private) static final String(package private) static final PatternTwo-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 StringThe 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 StringA template for the one supported way to run a compiler flixw did not download.(package private) static final Stringflixw's own releases.(package private) static final intHow much of a help screen counts as the header forparseReportedVersion(java.lang.String).(package private) static final int(package private) static final Duration(package private) static final StringA 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 StringThe URL written into every generated lock as a `#:schema` directive, and the `$id` of the schema itself.(package private) static final StringThe lock format's major version, which is not the wrapper's.(package private) static final intAdoptium answers in a few tens of KiB; this is room to spare, not a target.(package private) static final intLocks already reported on, so a second read in the same run stays quiet.(package private) static final StringWhere the generated documentation and the JSON Schema are published.(package private) static final StringOne 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 DurationBounds for the two child processes stage 0 runs for information rather than for work.(package private) static final StringGitHub'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 intThe 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 intThe interval flixw is tested on.(package private) static final Pattern(package private) static final StringWhere the stock compiler comes from when nothing says otherwise.(package private) static final StringThe name of the note stage 0 leaves for a completer, holding the verbs this project would actually dispatch.(package private) static final PatternA version token standing on its own, rather than one buried inside a longer word.(package private) static final String(package private) static final String -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescription(package private) static booleanacceptable(int f, String source) Below MIN_JAVA is always fatal: the compiler will not run.(package private) static Pathacquire(flixw.Lock lock) (package private) static StringaskedVersion(flixw.Lock lock, Path jar, flixw.Jvm jvm) The reported version for the verbs that print state; null whenever there is no jar.(package private) static booleanassetExists(String url) Does this release asset exist? A HEAD, so the download itself stays a single attempt.(package private) static intWaits for a child that owns the terminal, and guarantees it dies with us.(package private) static intbracketDelta(String line) How much this line opens or closes an inline array, counting only brackets outside quotes.(package private) static StringcachedVersionRecord(String identity) The version recordverbs(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 StringThe single normalization used for release tags, cache coordinates, and every version comparison.(package private) static StringcanonicalAttrs(String shipped) The line endings the flixw block pins for one shipped path.(package private) static StringcaptureHelp(Path javaExe, Path jar) The compiler's `--help`, bounded, as text.captureVerbs(String out, Path jar) (package private) static intcheck(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 intcheckCanonical(Path file, String canonical, String label) Compares a committed invariant file against the bytes this wrapper release ships.(package private) static intgitattributes 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(package private) static flixw.JvmchooseInstall(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 PathThe pinned compiler's own completion script, cached, or null if it has none.(package private) static PathcompilerPath(flixw.Lock lock) (package private) static StringcompletionScript(String shell) A completion script for one shell, on stdout.(package private) static int(package private) static voiddeleteTree(Path p) (package private) static void(package private) static StringA release tag in a URL path.(package private) static String(package private) static Path(package private) static flixw.Fail(package private) static Integer(package private) static StringfieldJson(flixw.LockField f, String indent) (package private) static PathfindJavaUnder(Path root) Layout differs per platform -- macOS nests a .jdk bundle -- so look rather than guess.(package private) static PathSearch upward from cwd for flix.toml, bounded above by the wrapper's own project.(package private) static IntegerRunsgit <args>in root; null when git is absent or the command fails to start.(package private) static HttpClientThe one HTTP client, pinned to HTTP/1.1.(package private) static StringOne bounded HTTPS GET returning text.(package private) static StringhumanSize(long bytes) A byte count as a person reads it; cached JARs and JDKs are always well above 1 KB.(package private) static booleaninsideCompilerCache(Path jar) True when a path names something inside the cache flixw fills with pinned compilers.(package private) static void(package private) static PathThe java recorded by the last successful install, if it is still there.(package private) static PathDownloads, verifies and unpacks one JDK into the wrapper cache, and returns its `java`.(package private) static voidinstallJdkVerb(List<String> argv) `./flixw wrapper --install-jdk`, so the choice need not wait for a failure.(package private) static booleanisKey(flixw.TomlEntry e, String table, String key) True when an entry is `table.key`.(package private) static booleanisMac()(package private) static boolean(package private) static booleanjavaPinAvailable(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.(package private) static StringjdkArch()aarch64 or x64 as Adoptium spells it, or null where it publishes nothing for us.(package private) static StringWindows gets a zip; nobody publishes a tar.gz for it.(package private) static voidjdkInstructions(int want) What to type on this OS, pointing at the same vendor flixw would fetch.(package private) static String(package private) static StringEnough JSON for flat string fields of one small, known response.(package private) static StringjsonObject(String json, String key) The first brace-balanced object under `"key":`.(package private) static StringjsonString(String s) JSON string literal.jvmOpts()Directories a JDK is commonly unpacked into.(package private) static voidInherit cwd and the three streams; propagate the child's status.(package private) static voidlistCache(flixw.Lock lock, flixw.Jvm jvm) Everything already sitting on this machine -- compilers and JDKs flixw itself cached, plus the JDKsknownInstalls()can already see without a network call.(package private) static List<flixw.LockField> lockFields(String table) The fields declared for one table, in lock order.(package private) static Path(package private) static StringThe published JSON Schema for lock.toml, rendered fromLOCK_SCHEMA.The tables the schema knows about, deduplicated, in lock order.(package private) static StringOne place that knows what a lock looks like, so the writer cannot drift by table.static voidThe one entry point.(package private) static StringmanifestVersion(Path manifest) The manifest is the human authority; disagreement stops us before the network.(package private) static void(package private) static voidmigrateFromFlixNames(Path target) Moves a project installed under the pre-0.20 names onto the current ones.(package private) static flixw.JvmnoJavaFound(int self, String pin) Nothing usable was found: say how to fix it, then offer to do it.(package private) static voidnoteUnknownLockKeys(String text, String where, String wroteIt) Keys the schema does not describe, reported once and never fatally.(package private) static int(package private) static booleanofferJdk(int want) Offers to fetch one only when there is somebody to answer.(package private) static booleanolderOrSame(String a, String b) Is `a` no newer than `b`? Both are the wrapper's own dotted versions.(package private) static flixw.PinparsePin(List<String> args, flixw.Lock existing) ./flixw pin [<owner>/<repo>] [<version>] [--java <version>], or./flixw pin --refresh.(package private) static StringparseReportedVersion(String help) The version the compiler says it is, read from the header of its own help.parseVerbs(String out) The verbs a help screen advertises, by three independent parses.(package private) static booleanpatternMatches(String pattern, String path) Does one .gitattributes pattern match one path flixw ships?(package private) static void(package private) static intReads<home>/releasewhen present and parseable; else runs the candidate once.(package private) static StringprobeVersion(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.(package private) static String(package private) static flixw.LockreadLockFields(String text, String where) Reads every keyLOCK_SCHEMAdeclares, keyed as `table.key` with the root table's keys unprefixed.(package private) static void(package private) static voidrecordCompletion(Path root, Path script) Same, for the path to the compiler's own completion script; absent means none.(package private) static voidrecordJava(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 voidrecordNote(Path root, String name, String body) (package private) static voidrecordVerbs(Path root, List<String> compilerVerbs) Records the verbs this project dispatches, for a completer to read.(package private) static StringRedacts credentials from a URL-shaped value before it is printed.(package private) static StringredactOpts(String v) The same, for JVM option strings, which can carry -Dhttps.proxyPassword=secret.(package private) static flixw.RefreshrefreshLock(Path root) (package private) static voidrefreshPin(Path root) `./flixw pin --refresh`.(package private) static booleanAt most one relaunch, guarded by an env marker, so a stale release file cannot loop.(package private) static void(package private) static StringreportedVersion(Path javaExe, Path jar, String identity) What the pinned compiler says its version is, or null if it will not say.(package private) static voidreportOverrideGap(flixw.Lock lock, Path jar) Says so whenFLIX_JARnames a compiler out of flixw's own cache.(package private) static voidreportVersionGap(flixw.Lock lock, String reported) Says so when the compiler is not the version the lock claims.(package private) static Path(package private) static flixw.AssetresolveRelease(String repo, String version) Resolves the compiler artifact for one repository and version, without asking an API anything.(package private) static flixw.JdkPackageresolveTemurin(int feature) Resolves the current Temurin release for this platform.(package private) static voidBest-effort rollback; a failed restore must not mask the failure being reported.(package private) static StringrewriteBase(String url) (package private) static voidroutingNotice(String verb, String compilerVersion) Which side handled a verb, under FLIXW_TRACE only.(package private) static StringrunCapture(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 booleansatisfiesJavaPin(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.(package private) static flixw.JvmselectJava(String pin) (package private) static voidselfCompile(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 Stringsha256(byte[] b) (package private) static String(package private) static PathThe .java file this stage 0 was launched from, or null when it is running as the compiled class out of the cache.Splits a key into its segments, respecting quotes, then unquotes and trims each one.(package private) static Path(package private) static boolean(package private) static StringstripComment(String line) Strips a trailing comment, ignoring '#' inside quotes.(package private) static StringAccepts the release tag where a version is expected:v0.75.2means0.75.2.(package private) static StringOne documented tokenizer: whitespace separates; '' and "" quote; \ escapes inside "" and bare.(package private) static StringtomlLookup(String text, String table, String key, String where) Reads one key from one TOML table.(package private) static flixw.TomlScanThe single TOML line scanner in stage 0.(package private) static void(package private) static booleantrace()(package private) static StringThe `x.x.x` that `[package].flix` is allowed to hold.unknownLockKeys(String text, String where) Every key in the file thatLOCK_SCHEMAdoes not describe, named the way a diagnostic names it, in file order and without repeats.(package private) static StringReturns whatever the unpacker said, for a diagnostic; success is judged separately.(package private) static String(package private) static void(package private) static voidupdateWrapper(Path root) Rewrites the invariant wrapper files from the running stage 0, leaving the project's compiler lock untouched.(package private) static voidupgradeWrapper(Path root) Moves this project to the newest published flixw.(package private) static voidValidated 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 voidvalidateJavaPin(String v, String where) A pin is a dotted number and nothing else: no ranges, no `latest`, no vendor.(package private) static voidvalidateUrl(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 StringvalidateVersion(String v, String where) (package private) static StringverbIdentity(Path jar, flixw.Lock lock, boolean override) Pinned compilers are identified by their locked digest; overrides by path+size+mtime.(package private) static PathVerb 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 PathversionFile(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 voidFLIXW010 and FLIXW011 are advisory: they are printed, they never set exit status.(package private) static void(package private) static voidwarnMissingJava(String javaPin) A pin naming a Java this machine does not have is written, and said out loud.(package private) static StringIOException.getMessage() is often bare the path; name the failure too.(package private) static PathResolves this file's own symlink chain without physicalizing unrelated directories.(package private) static void(package private) static voidwrapperNamespace(List<String> argv) flixw's own namespace.(package private) static StringwrapperUsage(String problem) (package private) static voidwrapperVerb(String verb, List<String> rest, Path root, flixw.Lock lock, Path jar, flixw.Jvm jvm, List<String> compilerVerbs) (package private) static voidwriteAtomic(Path file, String text) Same-directory temp plus an atomic move.(package private) static voidwriteEnvrcExample(Path target) Written once, then never touched again -- unlike every other file install writes.(package private) static voidwriteLocalIgnore(Path target) (package private) static voidwriteVersionRecord(String identity, String reported) A blank record is written when the header carried no version, so it is asked once.
-
Field Details
-
WRAPPER_VERSION
- See Also:
-
WRAPPER_DIR
- See Also:
-
MIN_JAVA
static final int MIN_JAVA- See Also:
-
SOURCE_FLOOR
static final int SOURCE_FLOORThe 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_CEILINGThe 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
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
-
HELP_CAP
static final int HELP_CAP- See Also:
-
WRAPPER_VERBS
-
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
-
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
Where the generated documentation and the JSON Schema are published.- See Also:
-
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
GitHub's own limits on the two path segments; a fork may live anywhere within them.- See Also:
-
JAVA_PIN_PATTERN
A feature release or an exact one, and nothing else -- no ranges, no vendor.- See Also:
-
LOCK_SCHEMA
Every key a lock may hold, in the order a generated lock writes them.requiredmeans 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
Locks already reported on, so a second read in the same run stays quiet. -
UPSTREAM_REPO
Where the stock compiler comes from when nothing says otherwise.- See Also:
-
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_CAPAdoptium answers in a few tens of KiB; this is room to spare, not a target.- See Also:
-
ADOPTIUM_API
- See Also:
-
ADOPTIUM_RELEASES
- See Also:
-
UNSAFE
-
VERSION_TOKEN
A version token standing on its own, rather than one buried inside a longer word. Built fromSEMVERISHso the two cannot drift into disagreeing about what a version is. -
HEADER_LINES
static final int HEADER_LINESHow much of a help screen counts as the header forparseReportedVersion(java.lang.String).- See Also:
-
COMMAND_ENTRY
Two-space indent, a lowercase name, then the column gap before its description. -
SHIM
- See Also:
-
CMD
- See Also:
-
SHIPPED
-
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
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
flixw's own releases. `latest/download` resolves without asking an API anything.- See Also:
-
COMPLETION_SHELLS
-
COMPL_BASH
- See Also:
-
COMPL_ZSH
- See Also:
-
COMPL_FISH
- See Also:
-
COMPL_PWSH
- See Also:
-
VERBS_NOTE
The name of the note stage 0 leaves for a completer, holding the verbs this project would actually dispatch. It lives besidelocal/javaand is machine-specific for the same reason: it describes a resolved compiler, not the project.- See Also:
-
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
-
w001
-
w002
-
w003
-
w004
-
w005
-
w006
-
w007
-
w008
-
w009
-
w010
FLIXW010 and FLIXW011 are advisory: they are printed, they never set exit status. -
w011
-
env
-
trace
static boolean trace() -
tr
-
validateVersion
-
stripTagPrefix
Accepts the release tag where a version is expected:v0.75.2means0.75.2. GitHub shows the tag, not the version. The releases page, the tag list, the archive links and the asset URLs all readv0.75.2, so copying from where the versions actually are gets you the tag every time -- and flixw itself builds"v" + versionto 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, sovNextis still a bad version rather than the versionNext, and the diagnostic keeps naming the real problem. Deliberately not applied to[package].flix: that field is Flix's, and Flix acceptsx.x.xalone. 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
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
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
-
why
IOException.getMessage() is often bare the path; name the failure too. -
redact
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
The same, for JVM option strings, which can carry -Dhttps.proxyPassword=secret. -
lockTables
The tables the schema knows about, deduplicated, in lock order. The root is "". -
lockSchemaJson
The published JSON Schema for lock.toml, rendered fromLOCK_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, andjsonString(java.lang.String)escapes them anyway -- the patterns are full of backslashes. -
lockFields
The fields declared for one table, in lock order. -
fieldJson
-
tableJson
-
jsonArray
-
jsonString
JSON string literal. Only the escapes RFC 8259 requires; every value here is ASCII. -
tomlScan
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
True when an entry is `table.key`. Dotted keys are resolved to their table bytomlScan(java.lang.String, java.lang.String), so both spellings arrive here already in the same shape. -
splitKey
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
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
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
Strips a trailing comment, ignoring '#' inside quotes. -
unquote
-
lockPath
-
readLock
-
readLockFields
Reads every keyLOCK_SCHEMAdeclares, 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
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
Every key in the file thatLOCK_SCHEMAdoes 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
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
-
sha256
-
sha256
-
checkRepo
-
encodeTag
A release tag in a URL path. Only '+' needs it; the rest of a version is path-safe. -
resolveRelease
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>.jarand `flix.jar`, with a HEAD each; the download that follows is still exactly one acquisition attempt for one artifact. -
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
Does this release asset exist? A HEAD, so the download itself stays a single attempt. -
parsePin
./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
True when a path names something inside the cache flixw fills with pinned compilers. -
reportOverrideGap
Says so whenFLIX_JARnames 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
-
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
Structural validation, so a malformed lock produces a FLIXW diagnostic rather than an uncaught IllegalArgumentException from URI.create deep in the download path. -
rewriteBase
-
acquire
-
download
-
runCapture
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
-
probe
Reads<home>/releasewhen present and parseable; else runs the candidate once. -
probeVersion
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
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
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
-
strictJava
static boolean strictJava() -
acceptable
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
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
-
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
aarch64 or x64 as Adoptium spells it, or null where it publishes nothing for us. -
jdkArchiveType
Windows gets a zip; nobody publishes a tar.gz for it. -
httpGet
One bounded HTTPS GET returning text. Metadata only; bytes go through download(). -
jsonField
Enough JSON for flat string fields of one small, known response. -
jsonObject
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
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
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
The java recorded by the last successful install, if it is still there. -
unpack
Returns whatever the unpacker said, for a diagnostic; success is judged separately.- Throws:
IOException
-
unzip
- Throws:
IOException
-
findJavaUnder
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
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
Nothing usable was found: say how to fix it, then offer to do it. -
installJdkVerb
`./flixw wrapper --install-jdk`, so the choice need not wait for a failure. -
jvmOpts
-
tokenize
One documented tokenizer: whitespace separates; '' and "" quote; \ escapes inside "" and bare. -
wrapperAnchor
Resolves this file's own symlink chain without physicalizing unrelated directories. -
resolveLinkChain
- Throws:
IOException
-
findRoot
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
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
Pinned compilers are identified by their locked digest; overrides by path+size+mtime. -
verbs
-
versionFile
Beside the verb record and keyed the same way, so a re-pin gets a fresh one. -
cachedVersionRecord
The version recordverbs(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 canonical0.75.3the cache directory names), without paying for a `--help` capture per cached jar on a verb that stays offline and fast. -
writeVersionRecord
A blank record is written when the header carried no version, so it is asked once. -
reportedVersion
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
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 taggedv0.75.4over a 0.75.2 build, an upstream re-upload -- is pinned, verified and run without a word, andinfogoes 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 throughcanonical(java.lang.String), because build metadata identifies a build rather than a release: a compiler built from0.75.3+stable.names.3reporting0.75.3is 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 --infoanddoctorprint 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
The compiler's `--help`, bounded, as text. The two parses read it separately. -
captureVerbs
-
parseReportedVersion
The version the compiler says it is, read from the header of its own help. Free, becauseverbs(java.nio.file.Path, java.nio.file.Path, java.lang.String)already runs--helpand 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 writesThe Flix Programming Language 0.75.2on 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
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
-
selfCompile
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
-
wrapperVerb
-
askedVersion
The reported version for the verbs that print state; null whenever there is no jar. -
report
-
listCache
Everything already sitting on this machine -- compilers and JDKs flixw itself cached, plus the JDKsknownInstalls()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
A byte count as a person reads it; cached JARs and JDKs are always well above 1 KB. -
checkCanonical
Compares a committed invariant file against the bytes this wrapper release ships. -
canonicalAttrs
The line endings the flixw block pins for one shipped path. -
patternMatches
Does one .gitattributes pattern match one path flixw ships? -
checkGitattributes
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
-
git
Runsgit <args>in root; null when git is absent or the command fails to start. -
check
Every check, printed; the count is the caller's to act on. -
writeAtomic
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
Best-effort rollback; a failed restore must not mask the failure being reported. -
pin
-
warnMissingJava
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
-
writeLocalIgnore
- Throws:
IOException
-
writeEnvrcExample
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
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
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
- Throws:
IOException
-
refreshPin
`./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
- Throws:
IOException
-
olderOrSame
Is `a` no newer than `b`? Both are the wrapper's own dotted versions. -
num
-
upgradeWrapper
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
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
-
completionScript
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 nextpinand 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 bargainBUILTIN_VERBSmakes, and stale in the same harmless way.- Parameters:
shell- one ofCOMPLETION_SHELLS- Returns:
- the script text, ending in a newline
-
recordVerbs
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 inrecordJava(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
Same, for the path to the compiler's own completion script; absent means none. -
recordNote
-
compilerCompletion
The pinned compiler's own completion script, cached, or null if it has none. Detection costs nothing and needs no version sniffing: picocli registersgenerate-completionas an ordinary subcommand, so it arrives in the verb setparseVerbs(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 thecomplete -Fregistration 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
The one entry point. Every failure inside is aflixw.Fail, which carries both theFLIXWnnncode 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
-
routingNotice
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
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
-
recordJava
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
At most one relaunch, guarded by an env marker, so a stale release file cannot loop. -
awaitWithReaper
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
Inherit cwd and the three streams; propagate the child's status.
-