Stop compiling the GPL crate GPUI drags in behind the framework
Lumbridge is Apache-2.0, and decision 0023 quietly made it link
GPL-3.0-or-later. `crates/gpui/Cargo.toml` at the pinned revision carries
`ztracing.workspace = true` -- unconditional, not optional, not behind a
feature -- and `ztracing` is GPL-3.0-or-later, as are the `zlog` and
`ztracing_macro` it pulls. `sum_tree` asks for it too. The path is the ordinary
Linux build, not an `--all-features` artefact and not a dev-dependency:
lumbridge -> gpui_platform -> gpui_linux -> gpui -> ztracing -> zlog
-> ztracing_macro
`cargo deny check licenses` failed on it, exit 4.
This is the second thing decision 0023 got wrong by reading the manifests of the
crates it added instead of resolving the graph; the first was believing there
were two Zed Git sources when there are five. Both were found by a gate that had
never been run.
There was no feature to turn off, so the choice was to relax the licence policy,
drop the framework, or stop compiling the crate. `ztracing` is now redirected by
a `[patch]` table at `crates/lumbridge-ztracing-shim`, a first-party
zero-dependency no-op under Apache-2.0. `zlog` and `ztracing_macro` were
reachable only through it and leave the lockfile with it.
The shim is small because the usage is: nine `#[instrument(skip_all)]` sites
across `gpui/src/svg_renderer.rs`, `sum_tree/src/sum_tree.rs` and
`sum_tree/src/cursor.rs`, and nothing else. Upstream's own crate compiles to
almost exactly this whenever the `ztracing` cfg is off, which is every build
that is not a Tracy profiling build, so no shipping behaviour is lost. It is a
proc-macro crate deliberately: such a crate can export nothing but proc macros,
so an upstream revision that starts using `ztracing::Span` or
`ztracing::info_span!` fails to compile and names the shim, rather than
resolving to something plausible.
`scripts/workspace-guard.sh` gained a third gate asserting, against Cargo.lock
rather than the manifest, that no `ztracing`, `zlog` or `ztracing_macro`
resolves to a Zed source and that the patch table is still present. `cargo deny`
already checks this, and the duplication is the point: `scripts/ci.sh`
downgrades a missing cargo-deny to a warning unless `LUMBRIDGE_CI_STRICT=1`, and
that is how the licence closure went ungated once already. A `[patch]` is an
unusually quiet thing to lose -- delete the table and everything still compiles,
still passes, and is GPL again.
Removing the three GPL rejections exposed a fourth that had been sitting beside
them and was never reported separately: `libbz2-rs-sys` under `bzip2-1.0.6`,
reached through async-compression <- http_client <- gpui. It is BSD-style and
permissive with no copyleft, and is allowed in `deny.toml` with that reasoning
written down. `cargo deny check licenses sources` is exit 0 for the first time.
The Git-source allowances in `deny.toml` are all still needed; the patched
crate's own source was `zed.git`, which `gpui` still requires.
Decision 0025 records the whole of it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPYebLiN2w4TqnHUYGdECq
This commit is contained in:
co-authored by
Claude Opus 5
parent
3cb5e1002c
commit
2aab0c4aab
@@ -0,0 +1,34 @@
|
||||
# This package is deliberately named `ztracing` and not `lumbridge-ztracing-shim`.
|
||||
#
|
||||
# A `[patch]` entry can only replace a package with one of the SAME NAME, so the
|
||||
# name is load-bearing: the root manifest's
|
||||
# `[patch."https://github.com/zed-industries/zed.git"]` table redirects the
|
||||
# `ztracing` that `gpui` and `sum_tree` ask for at this directory, and Cargo
|
||||
# matches the two by package name. The directory keeps the `lumbridge-` prefix
|
||||
# every other crate here has so that a reader browsing `crates/` can see whose
|
||||
# code it is; `src/lib.rs` explains why the package underneath answers to a
|
||||
# different name.
|
||||
[package]
|
||||
name = "ztracing"
|
||||
description = "A no-op stand-in for Zed's GPL-licensed ztracing, so Lumbridge's build graph stays Apache-2.0"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
publish = false
|
||||
|
||||
# A proc-macro crate, because the only symbol the graph reaches for is an
|
||||
# attribute macro. Nothing else can live in a crate declared this way, which is
|
||||
# the point: an upstream revision that starts using `ztracing::Span` or
|
||||
# `ztracing::info_span!` fails to compile here rather than quietly resolving.
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
# No dependencies. `syn` and `quote` would only be needed to parse an item this
|
||||
# macro then reassembles unchanged; returning the input `TokenStream` untouched
|
||||
# is both correct and three crates cheaper.
|
||||
[dependencies]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,103 @@
|
||||
//! A first-party `ztracing` that does nothing, so that Lumbridge stays Apache-2.0.
|
||||
//!
|
||||
//! Lumbridge is Apache-2.0, and `DISTRIBUTION.md` treats the licence closure of
|
||||
//! everything it links as a hard gate rather than a preference. Decision 0023
|
||||
//! moved the UI framework from published crates.io releases to a pinned
|
||||
//! revision of Zed, and that move brought more than `gpui` with it: it brought
|
||||
//! `gpui`'s own workspace-internal dependencies, which are not published
|
||||
//! anywhere and were never chosen one by one.
|
||||
//!
|
||||
//! One of them is **GPL-3.0-or-later**. `crates/gpui/Cargo.toml` at the pinned
|
||||
//! revision carries `ztracing.workspace = true` -- unconditional, not optional,
|
||||
//! not behind a feature -- and `ztracing` in turn pulls `zlog` and
|
||||
//! `ztracing_macro`, both also GPL-3.0-or-later. `sum_tree` asks for `ztracing`
|
||||
//! too. The path is short and entirely inside the build we ship:
|
||||
//!
|
||||
//! ```text
|
||||
//! lumbridge -> gpui_platform -> gpui_linux -> gpui -> ztracing -> zlog
|
||||
//! -> ztracing_macro
|
||||
//! ```
|
||||
//!
|
||||
//! This is not an `--all-features` artefact and not a dev-dependency. It is the
|
||||
//! ordinary Linux build, and `cargo deny check licenses` failed on it with
|
||||
//! `rejected: license is not explicitly allowed` for GPL-3.0-or-later. There is
|
||||
//! no feature flag to turn off, so there were three ways out: relax the licence
|
||||
//! policy, drop the framework, or stop compiling those crates. The first
|
||||
//! trades the product's licence for a lint's convenience and the second throws
|
||||
//! away decision 0023. This crate is the third.
|
||||
//!
|
||||
//! # What it replaces
|
||||
//!
|
||||
//! The root manifest's `[patch."https://github.com/zed-industries/zed.git"]`
|
||||
//! table redirects the `ztracing` package -- and only that package -- at this
|
||||
//! directory. Nothing else about the pinned revision changes: `gpui` and
|
||||
//! `gpui_platform` still resolve to
|
||||
//! `ce48461eaadd16c65c31f835511ab96bd3b6e746`, and `scripts/workspace-guard.sh`
|
||||
//! still asserts it. Because `zlog` and `ztracing_macro` were reachable ONLY
|
||||
//! through `ztracing`, patching the one crate removes all three from the
|
||||
//! resolved graph; they no longer appear in `Cargo.lock` at all.
|
||||
//!
|
||||
//! # The entire API surface the graph uses
|
||||
//!
|
||||
//! Every reference to `ztracing` or `zlog` across the eighteen Zed packages in
|
||||
//! Lumbridge's resolved graph, found by grepping the vendored checkout rather
|
||||
//! than by reading manifests:
|
||||
//!
|
||||
//! - `gpui/src/svg_renderer.rs`, two sites: `#[ztracing::instrument(skip_all)]`
|
||||
//! - `sum_tree/src/sum_tree.rs` and `sum_tree/src/cursor.rs`:
|
||||
//! `use ztracing::instrument;`, then seven `#[instrument(skip_all)]` sites
|
||||
//! - `sum_tree/src/sum_tree.rs:1401`: `zlog::init_test()` -- inside `#[cfg(test)]`,
|
||||
//! reached through a dev-dependency that Cargo never resolves for a
|
||||
//! non-workspace package, which is why no `zlog` shim is needed
|
||||
//!
|
||||
//! That is the whole of it: one attribute macro, always with `skip_all`. So
|
||||
//! [`instrument`] is an attribute proc-macro that returns the item it was given,
|
||||
//! byte for byte, and discards its arguments. Upstream's own crate does almost
|
||||
//! exactly this when the `ztracing` cfg is off -- which is to say, in every
|
||||
//! build that is not a Tracy profiling build -- so the shim is not a
|
||||
//! behavioural downgrade of a shipping feature. It is the same nothing,
|
||||
//! reimplemented under a licence we can distribute.
|
||||
//!
|
||||
//! # What a maintainer must check when the gpui revision is bumped
|
||||
//!
|
||||
//! **Whether upstream started using a `ztracing` symbol this crate does not
|
||||
//! provide.** That is the one way this arrangement can go wrong, and the design
|
||||
//! makes it go wrong loudly: a proc-macro crate can export nothing except proc
|
||||
//! macros, so `ztracing::Span`, `ztracing::info_span!`, `ztracing::init()`,
|
||||
//! `ztracing::Level` or `ztracing::field` all fail to resolve at compile time
|
||||
//! with `E0432`/`E0433` naming this crate. There is no silent fallback and no
|
||||
//! runtime surprise; the build stops.
|
||||
//!
|
||||
//! When it does, the fix is not to widen this crate by reflex. Read the new
|
||||
//! upstream use first: if it is another zero-cost annotation, add the
|
||||
//! corresponding no-op here (which will mean splitting this into a normal
|
||||
//! library crate plus a proc-macro crate, since the two cannot share one
|
||||
//! package). If instead upstream has started routing real telemetry through
|
||||
//! `ztracing`, that is a decision record, because Lumbridge ships no telemetry
|
||||
//! by default and `AGENTS.md` says so.
|
||||
//!
|
||||
//! Also re-run `cargo deny check licenses sources` and confirm the bump has not
|
||||
//! introduced a *second* GPL crate on a different path. The lesson of decision
|
||||
//! 0023 -- recorded there, in the correction about there being five Zed Git
|
||||
//! sources rather than two -- is that reading the manifests of the crates you
|
||||
//! added never finds a transitive dependency. Only resolving the graph does.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
/// Zed's `#[instrument]`, with the tracing removed.
|
||||
///
|
||||
/// Expands to the annotated item unchanged and drops the attribute arguments.
|
||||
/// Every call site in Lumbridge's graph passes `skip_all`, which asks for no
|
||||
/// fields to be recorded, so there is nothing here to preserve even in
|
||||
/// principle.
|
||||
///
|
||||
/// Deliberately accepts any argument tokens without parsing them: a future
|
||||
/// upstream `#[instrument(name = "...")]` should keep compiling, because the
|
||||
/// failure mode worth having is a missing *symbol*, which is loud, and not a
|
||||
/// spuriously rejected argument list, which would only be noise.
|
||||
#[proc_macro_attribute]
|
||||
pub fn instrument(_arguments: TokenStream, item: TokenStream) -> TokenStream {
|
||||
item
|
||||
}
|
||||
Reference in New Issue
Block a user