Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/provider-contract-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Every field below is required on the wires that define it, except the optional `
| `run` | **Optional**, **wire `1.2.0`+**: present on every `run`-step hook context (any phase) and absent (never null) everywhere else. An object with the required keys `env`, `args` and `timeout_ms`, plus `watch` from wire `1.3.0` and `outcome_file` from wire `1.5.0` (see below) |
| `cache_dir` | **Wire `1.3.0`+, required there, never null**: the provider's persistent cache directory, on every context (commands and hooks). See [Provider cache](#provider-cache). The key doesn't exist below `1.3.0` |
| `env_file` | **Wire `1.3.0`+, required there**: on a `before generate`, `after generate` or `before build` hook, an absolute path the hook may write its environment contribution to; null on every other hook and on commands. See [Environment contributions](#environment-contributions). The key doesn't exist below `1.3.0` |
| `final_step` | **Wire `1.4.0`+, required there**: on a hook, the last lifecycle step of the CLI command that runs it (see [§6](#6-hook-execution)): `"generate"` (`labelle generate`, and the legacy `labelle ios` subcommand, which only runs the generate hooks), `"build"`, `"run"` (`labelle run`, every watched rebuild included) or `"bundle"`. It is always a step the hook's own step leads to: any value on a `generate` hook, never `"generate"` on a `build` hook, and exactly its own step on a `run` or `bundle` hook; anything else is an error. Null on a command. The key doesn't exist below `1.4.0` |
| `final_step` | **Wire `1.4.0`+, required there**: on a hook, the last lifecycle step of the CLI command that runs it (see [§6](#6-hook-execution)): `"generate"` (`labelle generate`), `"build"`, `"run"` (`labelle run`, every watched rebuild included) or `"bundle"`. It is always a step the hook's own step leads to: any value on a `generate` hook, never `"generate"` on a `build` hook, and exactly its own step on a `run` or `bundle` hook; anything else is an error. Null on a command. The key doesn't exist below `1.4.0` |

The `run` object holds the `labelle run` options for a hook that wraps or replaces the launch:

Expand Down Expand Up @@ -124,7 +124,7 @@ A `before generate`, `after generate` or `before build` hook receives `env_file`

**Scope.** The merged environment reaches every later zig invocation and hook process of the same command: the generation-time fingerprint pass (`zig build --list-steps`, which configures the generated build and so is the first toolchain consumer: a `before generate` contribution reaches it, an `after generate` one does not), the core `zig build` compile, and every later hook and replacement (`after generate`, `before`/`replace`/`after build`, `bundle` and `run` hooks). It never reaches the build of a provider's own tool (`zig build <build_step>` in the provider's package), which runs on the plain inherited environment plus the CLI's cache variables, so a provider cannot change how other providers' tools are built. The game the core `labelle run` launches keeps its own environment (the inherited one plus the run options); a provider that must reach the game uses a `run` replacement. The runtime `SDL2.dll` the core stages beside a desktop exe is looked up through `LABELLE_SDL2_LIB` as the compile saw it, so a contributed value is honoured.

**No silent bypass.** A build path that cannot carry a provider's contribution or its optimize default refuses before any hook, generation or build, naming the hook or package. `--docker` builds in a container the contributions never reach, so a `build`, `run` or `bundle` (any command that reaches the container build) whose plan has a hook that may contribute (one in a contributing slot whose provider negotiates `1.3.0` or newer) is refused; `labelle generate --docker` is not, since it stops after generation and its host-side fingerprint pass gets the contributions, and neither is a build the target owner replaces, since its `replace build` hook gets them like every hook: `--docker doesn't carry provider environment contributions; build without --docker`. (`--docker` does pass the effective optimize mode as `-Doptimize`.) The legacy `labelle ios` subcommand runs its own `zig build` after generation, with neither, so it is refused when the plan has such a hook or the target's owner declares an optimize default; `labelle build --platform=ios` carries both.
**No silent bypass.** A build path that cannot carry a provider's contribution or its optimize default refuses before any hook, generation or build, naming the hook or package. `--docker` builds in a container the contributions never reach, so a `build`, `run` or `bundle` (any command that reaches the container build) whose plan has a hook that may contribute (one in a contributing slot whose provider negotiates `1.3.0` or newer) is refused; `labelle generate --docker` is not, since it stops after generation and its host-side fingerprint pass gets the contributions, and neither is a build the target owner replaces, since its `replace build` hook gets them like every hook: `--docker doesn't carry provider environment contributions; build without --docker`. (`--docker` does pass the effective optimize mode as `-Doptimize`.) The legacy `labelle ios` subcommand, which ran its own `zig build` carrying neither and was refused for both, left the core with RFC cli#471 I5; `labelle build --platform=ios` carries both.

### Run outcome

Expand Down
16 changes: 7 additions & 9 deletions docs/provider-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,13 @@ build` hook signed, stripped or patched in `zig-out/` is what runs.

- No JSON progress relay or validation for hook (or command) stdout yet: the
child's stdio is inherited exactly as for provider commands.
- The legacy `labelle ios …` subcommand handler is not a hook point for
`build`/`run`; it only shares the `generate` hooks. It leaves with platform
extraction (its target already goes through the resolver, so it needs the
pinned provider like `--platform=<t>`). `labelle android …` left with
cli#405: it is the `android` provider's namespace, and `--platform=android`
runs the ordinary hook points.
- A provider target other than the legacy `ios` run branch must
replace `run` (`NoRunReplacement`, [provider
targets](provider-targets.md#labelle-run)).
- `labelle android …` left with cli#405 and `labelle ios …` with RFC cli#471
I5: neither is a built-in, and `--platform=android` / `--platform=ios` run
the ordinary hook points of their pinned provider
([labelle-ios](https://github.com/labelle-toolkit/labelle-ios) for `ios`).
- Every provider target must replace `run` (`NoRunReplacement`, [provider
targets](provider-targets.md#labelle-run)); the core has no launch of its
own for any of them.
- `labelle bundle` for the core `desktop` target is still refused on Linux
and Windows — before any install or build — because the core packager is
macOS-only and no hook can replace it (nobody may own `desktop`). A
Expand Down
13 changes: 7 additions & 6 deletions docs/provider-local-dispatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ Run `labelle <namespace> <command> [arguments...]` from a project directory or
one of its descendants. Discovery walks to the nearest `project.labelle` and
reads manifests from that project's `.plugins`; it never searches global pins.
Built-ins take precedence, followed by package namespaces, then directory
shorthand. A package cannot claim a built-in namespace. The existing `ios`
and `wasm` commands remain reserved until their extraction lands; their
target already resolves like `--platform=<t>` does, through the pinned
provider that declares it ([provider targets](provider-targets.md)).
`android` left the reserved list with its extraction (cli#405): the
`android` package declares it as its namespace. A first word that is no
shorthand. A package cannot claim a built-in namespace. No platform word
is reserved any more: `android` left the list with its extraction
(cli#405; the `android` package declares it as its namespace), `wasm` with
the legacy browser commands (RFC cli#466 PR B), and `ios` when iOS moved
into [labelle-ios](https://github.com/labelle-toolkit/labelle-ios) (RFC
cli#471 I5). Their targets resolve like any other, through the pinned
provider that declares them ([provider targets](provider-targets.md)). A first word that is no
built-in, no pinned package's namespace and no directory is an unknown
command; when the cached schema-2 registry names a package declaring it as
a namespace, the diagnostic says so instead (`(registry: <package>)`).
Expand Down
33 changes: 19 additions & 14 deletions docs/provider-targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ and the CLI carries no Android code (cli#405). The other platform packages
## Resolution

Every command that runs the project pipeline (`generate`, `build`, `run`,
`bundle`, and the legacy `ios` subcommand) resolves
`bundle`) resolves
one target, in two halves, before anything is generated, locked or built:

1. The requested name is `--platform=<t>` when given, else the project's
Expand Down Expand Up @@ -201,9 +201,9 @@ explicit line:

- A provider target whose name is a schema platform (`wasm`, `android`,
`ios`) is handed to the assembler as `--platform <name>`, exactly as
before. The legacy pipeline branch that keys on the enum (`ios`) keeps
working for it; `android` and `wasm` have none left, so their providers
replace `run` (see [`labelle run`](#labelle-run)).
before. No pipeline branch keys on the enum any more (the last, the `ios`
simulator launch, left with RFC cli#471 I5), so their providers replace
`run` (see [`labelle run`](#labelle-run)).
- A provider target outside the enum can only be generated for by its
provider's `replace` hook on `generate`. Without one the command stops
before the assembler runs:
Expand All @@ -227,10 +227,11 @@ slice, and `provider_settings.zig` is untouched.

## `labelle run`

The CLI launches only the core `desktop` target on this host, plus the
legacy run branch it still carries (the `ios` simulator). The core's own
browser serve left in 3.0: the `web` provider's `replace run` hook serves
`wasm` now.
The CLI launches only the core `desktop` target on this host. The core's
own browser serve left in 3.0 (the `web` provider's `replace run` hook
serves `wasm` now), and its `ios` simulator launch with RFC cli#471 I5
([labelle-ios](https://github.com/labelle-toolkit/labelle-ios)'s `replace
run` hook launches it now).
Every other provider target is launched by its provider, so it must have a
`replace` hook on `run`, otherwise:

Expand Down Expand Up @@ -312,8 +313,8 @@ package injection (RFC #406 "Migration", #410). The user-facing walkthrough,
with Flying Platform as the worked example, is
[Migrating a project to labelle CLI 2.0](migrating-to-2.0.md):

- A project that builds for `wasm`, `android` or `ios` — through `.platform`,
`--platform=<t>`, or `labelle ios …` — must add the package that declares that target to
- A project that builds for `wasm`, `android` or `ios` — through `.platform`
or `--platform=<t>` — must add the package that declares that target to
`.plugins` (as `.repo = "github.com/<owner>/<name>"`) and pin it
(`labelle providers resolve`, then `--accept`). Until it does, those
commands fail with the no-provider error above. Commit
Expand All @@ -323,10 +324,14 @@ with Flying Platform as the worked example, is
([pins](provider-github-pins.md#fresh-checkouts-and-ci-labelle-providers-fetch)).
- The target name is unchanged: `--platform=wasm` stays `--platform=wasm`,
because the web provider declares the target `wasm`. Only the pin is new.
- The legacy `labelle ios` command word stays a reserved built-in until its
extraction lands; it routes its target through the same resolver. The
legacy `labelle wasm serve|export` left the core in 3.0: `wasm` is no
reserved word any more ([migrating to 3.0](migrating-to-3.0.md)).
- The legacy `labelle wasm serve|export` left the core in 3.0: `wasm` is no
reserved word any more ([migrating to 3.0](migrating-to-3.0.md)). The
legacy `labelle ios` subcommand followed (RFC cli#471 I5): the word is an
unknown command unless a pinned package declares it as a namespace, and
iOS builds, runs and bundles through
[labelle-ios](https://github.com/labelle-toolkit/labelle-ios) with
`--platform=ios`; its settings (formerly the `.ios` block, which the CLI
now ignores) live in `providers/ios.json`.
- `labelle android …` is no built-in any more (cli#405): it is the `android`
provider's namespace. Without that package pinned, `labelle android` is an
unknown command — or, inside a project, when the one document the target
Expand Down
6 changes: 6 additions & 0 deletions docs/rfc-ios-platform.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# RFC: iOS Platform Support

> **Status: superseded.** iOS left the CLI core with RFC cli#471 I5: the
> `labelle ios` subcommand, the simulator deploy and the `.ios` block are
> gone. iOS is the [labelle-ios](https://github.com/labelle-toolkit/labelle-ios)
> provider now (`--platform=ios`, settings in `providers/ios.json`). This
> document is kept as the historical design.

## Problem Statement

labelle-cli v1.x recognizes `--platform=ios` and generates callback-based Zig code via sokol's `mobile.txt` template, but the platform is non-functional. A developer who runs `labelle build --platform=ios` gets a binary that **cannot run on iOS** because:
Expand Down
16 changes: 6 additions & 10 deletions src/agnostic_guard_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ const allowed_words = [_][]const u8{ "macos", "windows", "linux", "darwin", "win
/// `cli/runner.zig` came clean: 18 entries. RFC cli#471 X2 moved the
/// vendored stb single-headers to `vendor/stb/` (third-party code lives
/// outside `src/`), taking `cli/stb_image.h`'s upstream comment with it:
/// 17 entries.
/// 17 entries. RFC cli#471 I5 removed iOS from the core (the `labelle-ios`
/// provider owns it now): `cli/ios.zig` went with its file, and
/// `cli/args.zig`, `cli/pipeline/args_resolve.zig`, `cli/pipeline/run.zig`
/// and `cli/provider_dispatch.zig` came clean: 12 entries.
/// Shrink only: an entry whose file is clean fails the test until it is
/// removed. Note the path scan: an entry
/// under `cli/android/` or named `cli/ios.zig` stays dirty until the file is
Expand All @@ -114,21 +117,16 @@ const allowed_files = [_][]const u8{
"agnostic_guard_test.zig",
// Legacy platform, store, package and backend sites (RFC #406 "Migration").
"cli.zig",
"cli/args.zig",
"cli/config.zig",
"cli/docker.zig",
"cli/doctor.zig",
"cli/ios.zig",
"cli/pipeline.zig",
// Split out of `cli/pipeline.zig` (moves only): the stage code they
// carry names the legacy targets, backends and their toolchains.
"cli/pipeline/args_resolve.zig",
"cli/pipeline/build.zig",
"cli/pipeline/generate.zig",
"cli/pipeline/install.zig",
"cli/pipeline/run.zig",
"cli/project_config.zig",
"cli/provider_dispatch.zig",
"cli/sdl_provision.zig",
"cli/upgrade.zig",
};
Expand Down Expand Up @@ -514,8 +512,8 @@ test "host OS names never flag" {
test "the allowlist matches Windows-style walker paths, one file per entry" {
try std.testing.expect(allowedIndex("cli/pipeline.zig") != null);
try std.testing.expect(allowedIndex("cli\\pipeline.zig") != null);
try std.testing.expect(allowedIndex("cli\\pipeline\\run.zig") != null);
try std.testing.expect(allowedIndex("cli/ios.zig") != null);
try std.testing.expect(allowedIndex("cli\\pipeline\\generate.zig") != null);
try std.testing.expect(allowedIndex("cli/sdl_provision.zig") != null);
// A new file under a legacy directory is NOT exempt.
try std.testing.expect(allowedIndex("cli/pipeline/not_yet_written.zig") == null);
try std.testing.expect(allowedIndex("cli/pipeline/") == null);
Expand Down Expand Up @@ -597,13 +595,11 @@ test "a platform in the path is a finding, and keeps an allowlist entry dirty" {
// clean contents: the entry is only stale once the file is moved or
// renamed.
try scan.file("cli/sdl_provision.zig", "const x = 1;\n");
try scan.file("cli\\ios.zig", "");
try std.testing.expectEqual(@as(usize, 7), scan.offenders.items.len);
var stale: std.ArrayList([]const u8) = .empty;
defer stale.deinit(gpa);
try scan.stale(&stale);
try std.testing.expect(!containsString(stale.items, "cli/sdl_provision.zig"));
try std.testing.expect(!containsString(stale.items, "cli/ios.zig"));
try std.testing.expect(containsString(stale.items, "cli/upgrade.zig"));
}

Expand Down
Loading
Loading