You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
dylint: lint crates' ui fixtures cannot load their .so/.dll on windows runners (CARGO_BUILD_TARGET vs dylint's probe path) #1373
On a windows-latest runner, the Dylint workflow's "Test Dylint libraries" step — which runs each lint crate's own compiletest ui fixtures — fails:
diff of stderr:
-error: use fbuild_core::path::NormalizedPath::display_slash() instead of hand-rolled ... rewrite
- --> $DIR/disallowed.rs:5:16
...
+error: could not load library
+ `D:/a/fbuild/fbuild/target/dylint-tests/debug/ban_manual_slash_normalize@nightly-2026-04-16.dll`:
+ LoadLibraryExW failed
The lint never loads, so the fixture compiles clean and compiletest reports the expected diagnostic as missing.
What is going on
Every lint crate's #[test] fn ui already documents half of this:
// Dylint 6.0.1 looks for its test library directly under// <target>/debug, while Soldr selects the host through// CARGO_BUILD_TARGET and Cargo writes to <target>/<host>/debug.unsafe{ std::env::remove_var("CARGO_BUILD_TARGET");}
Clearing the variable is not sufficient on the runner: it happens inside the test process, after cargo has already decided where to place the artifact. The error path the harness then probes (<target>/dylint-tests/debug/...dll) is not where the .dll was written (<target>/dylint-tests/x86_64-pc-windows-msvc/debug/...).
Two details suggest looking closer before assuming it is only a path問題:
LoadLibraryExW also fails when a dependent DLL cannot be resolved, not only when the file is absent — so a missing-file diagnosis should be confirmed rather than inferred from the message.
The PATH the harness builds for the driver invocation begins with a stray ; and contains a lone C; fragment, i.e. it looks mangled somewhere in the assembly. That may be incidental to Git Bash path conversion, or may be the actual cause.
Why it is filed rather than fixed
#1359 added a windows-latest Dylint leg so Windows-gated source is actually linted. The lints themselves are clean there — the full workspace sweep passes. Only the lint crates' own self-tests fail, and those assert platform-independent behavior (each lint fires on its fixture) already covered by the ubuntu leg. So that step is scoped to ubuntu, and this is tracked here instead of blocking the gate.
Worth fixing if someone wants the ui fixtures exercised on Windows too — mostly as a guard against a lint whose diagnostic rendering differs per-OS, which is a real if narrow risk given several of these lints are about path spelling.
Likely fixes
Set CARGO_BUILD_TARGET= (empty) for the step itself rather than clearing it inside the test, so cargo places the artifact where the harness looks.
Or point the harness at the real location by setting DYLINT_LIBRARY_PATH to <target>/<host>/debug on Windows.
Confirm first whether the .dll exists at the probed path — that separates "wrong directory" from "dependent DLL missing", which need different fixes.
Two attempts, two CI rounds, same failure. Recording what is now established so the next attempt starts from facts rather than my original guess — which was wrong.
The issue's original hypothesis is wrong
I blamed a CARGO_BUILD_TARGET path mismatch. That is real but not sufficient: with CARGO_BUILD_TARGET set, with an explicit --target build, and with the env var unset, the ui tests pass on a Windows host. All 27 lint crates pass locally through the exact CI step script. The bug is runner-specific.
What is established
Two target-dir layouts coexist. Reproduced locally — after one cargo test run, $CARGO_TARGET_DIR contains both:
debug/
x86_64-pc-windows-msvc/
soldr builds the test binary under <target>/<triple>/debug, while each lint's fn ui clears CARGO_BUILD_TARGET so the library it rebuilds lands in <target>/debug — the only place Dylint 6.0.1 looks. Notably, unsetting CARGO_BUILD_TARGET for the whole step does not collapse them: soldr injects --target regardless, because the step invokes soldr cargo by name rather than through the proxy.
The failure is a missing dependency, not a missing file. compiletest invokes the driver with
LoadLibraryExW failed names the library, which reads like the file is absent; the file is there. This is the distinction the issue text told me to confirm first and I then failed to apply.
Ruled out
hypothesis
result
PATH separator mangling — joining native CARGO_HOME to a POSIX $PATH with :
Real bug, fixed in #1377, but not this failure — Windows leg failed identically after
So the missing dependency is in neither target tree. Remaining candidates worth checking, in order:
Which toolchain instance the library was linked against. The driver comes from DYLINT_DRIVER_PATH, the library from a cargo build under RUSTUP_TOOLCHAIN=nightly-2026-04-16, and on CI RUSTUP_HOME is setup-soldr's private root. If those resolve to two different rustc_driver-*.dll builds, the library's dependency is a specific hash of that dylib and the PATH points at the other one. This is my best remaining guess and it is checkable by listing rustc_driver* under both roots on the runner.
Dump the actual dependency. One CI step running dumpbin /dependents (or objdump -p) on the built .dll, plus a directory listing of both debug trees, would end the guessing entirely. Cheaper than another hypothesis.
Make the layouts genuinely single by having the step invoke the rustup cargo proxy directly instead of soldr cargo — there is precedent in the same workflow, where the sweep step bypasses soldr for a similar reason. I could not test this locally because the repo hook blocks bare cargo.
Whoever picks this up: start with (2). It converts the remaining question from a guess into a reading.
Reopened — GitHub auto-closed this when #1377 merged, because my first commit on that branch carried a Closes FastLED/fbuild#1373 trailer written when I still thought I had the fix. I rescoped the PR after two failed attempts but the trailer stayed in the squashed commit message, so the close was mechanical, not a claim.
This is not fixed. The evidence comment above stands: two target-dir layouts coexist, the failure is a missing dependency rather than a missing file, and both hypotheses I tried are ruled out. Start with dumping the actual dependency list on the runner — it converts the remaining question from a guess into a reading.
What #1377 did land: the cygpath PATH-join hardening (a real latent bug on Windows, just not this one), and a de-flake of a test I had introduced in #1368.
Activity
fix(ci): put the untargeted deps dir on PATH for the dylint ui tests
fix(ci): de-flake my own ETXTBSY retry test; stop guessing at #1373
zackees commentedon Aug 23, 2026
Two attempts, two CI rounds, same failure. Recording what is now established so the next attempt starts from facts rather than my original guess — which was wrong.
The issue's original hypothesis is wrong
I blamed a
CARGO_BUILD_TARGETpath mismatch. That is real but not sufficient: withCARGO_BUILD_TARGETset, with an explicit--targetbuild, and with the env var unset, the ui tests pass on a Windows host. All 27 lint crates pass locally through the exact CI step script. The bug is runner-specific.What is established
Two target-dir layouts coexist. Reproduced locally — after one
cargo testrun,$CARGO_TARGET_DIRcontains both:soldr builds the test binary under
<target>/<triple>/debug, while each lint'sfn uiclearsCARGO_BUILD_TARGETso the library it rebuilds lands in<target>/debug— the only place Dylint 6.0.1 looks. Notably, unsettingCARGO_BUILD_TARGETfor the whole step does not collapse them: soldr injects--targetregardless, because the step invokessoldr cargoby name rather than through the proxy.The failure is a missing dependency, not a missing file. compiletest invokes the driver with
and the library it loads is at
LoadLibraryExW failednames the library, which reads like the file is absent; the file is there. This is the distinction the issue text told me to confirm first and I then failed to apply.Ruled out
CARGO_HOMEto a POSIX$PATHwith:debug+debug/depsmissing from PATHCARGO_BUILD_TARGETset/unset/explicit--targetlocallySo the missing dependency is in neither target tree. Remaining candidates worth checking, in order:
DYLINT_DRIVER_PATH, the library from a cargo build underRUSTUP_TOOLCHAIN=nightly-2026-04-16, and on CIRUSTUP_HOMEis setup-soldr's private root. If those resolve to two differentrustc_driver-*.dllbuilds, the library's dependency is a specific hash of that dylib and the PATH points at the other one. This is my best remaining guess and it is checkable by listingrustc_driver*under both roots on the runner.dumpbin /dependents(orobjdump -p) on the built.dll, plus a directory listing of bothdebugtrees, would end the guessing entirely. Cheaper than another hypothesis.soldr cargo— there is precedent in the same workflow, where the sweep step bypasses soldr for a similar reason. I could not test this locally because the repo hook blocks barecargo.Whoever picks this up: start with (2). It converts the remaining question from a guess into a reading.
fix(ci): de-flake the ETXTBSY retry test and harden the Windows PATH …
zackees commentedon Aug 23, 2026
Reopened — GitHub auto-closed this when #1377 merged, because my first commit on that branch carried a
Closes FastLED/fbuild#1373trailer written when I still thought I had the fix. I rescoped the PR after two failed attempts but the trailer stayed in the squashed commit message, so the close was mechanical, not a claim.This is not fixed. The evidence comment above stands: two target-dir layouts coexist, the failure is a missing dependency rather than a missing file, and both hypotheses I tried are ruled out. Start with dumping the actual dependency list on the runner — it converts the remaining question from a guess into a reading.
What #1377 did land: the
cygpathPATH-join hardening (a real latent bug on Windows, just not this one), and a de-flake of a test I had introduced in #1368.