[bug] Rust toolchain `scaffold_docker` leaves an empty src/main.rs in library crates, breaking the build with E0601
**Describe the bug**
The Rust toolchain plugin's `scaffold_docker` unconditionally writes an **empty** `src/main.rs`
(and `src/lib.rs`) into every project during the `Configs` scaffold phase. For a **library-only**
crate (has `src/lib.rs`, no `src/main.rs`, no explicit `[[bin]]`), this empty `src/main.rs` is
never removed by the `Sources` phase — that overlay copies real files but does not prune skeleton
files with no real counterpart. In the build stage, cargo's default binary auto-discovery
(`autobins`) then picks up the stray empty `src/main.rs` as a binary target with no `fn main` and
fails:
```
error[E0601]: `main` function not found in crate `my_lib`
= note: consider adding a `main` function to `packages/my-lib/src/main.rs`
error: could not compile `my-lib` (bin "my-lib") due to 1 previous error
```
The referenced `src/main.rs` does not exist in the repository — it is created only inside the
build by the scaffold step. This is easy to miss because a plain host-side `cargo build` (no stub
present) passes; it only surfaces through the docker scaffold flow.
> **Verified with moon 2.4.2.** `moon docker scaffold my-app` writes a 0-byte
> `.moon/docker/configs/packages/my-lib/src/main.rs`; the `sources` skeleton contains only the real
> `lib.rs`. A `docker build` (which overlays `configs` then `sources`) leaves the empty `main.rs` in
> place, and `moon run my-app:build` fails with `error[E0601]` / `could not compile my-lib (bin "my-lib")`.
>
> One subtlety: the failure only occurs when something actually **compiles `my-lib`'s targets** (the
> phantom `bin`) — a whole-workspace `cargo build`, `cargo build --package my-lib` / `moon run my-lib:build`,
> or an app build whose task depends on the library build (as the attached repro wires it). A scoped
> `cargo build --package my-app` builds only my-lib's *library* target and does **not** surface the bug.
The offending code is in `github.com/moonrepo/plugins`, `toolchains/rust/src/tier1.rs`
(`rust_toolchain` v1.0.6, the version moon 2.4.2 resolves; still present on `master`):
```rust
// Cargo requires either `lib.rs` or `main.rs` during
// the workspace/configs phase, which isn't copied till the
// sources phase. Because scaffolding may attempt to run
// Cargo commands, it will fail without these files!
if input.phase == ScaffoldDockerPhase::Configs && input.project.is_some() {
let lib_file = input.output_dir.join("src/lib.rs");
let main_file = input.output_dir.join("src/main.rs");
fs::write_file(&lib_file, "")?; // empty
fs::write_file(&main_file, "")?; // empty — no `fn main`
...
}
```
**Steps to reproduce**
A minimal moon workspace with two reproduction paths:
**https://github.com/YassineElbouchaibi/moon-rust-scaffold-repro**
(downloadable zip: https://github.com/YassineElbouchaibi/moon-rust-scaffold-repro/releases/download/v1.0.1/moon-rust-scaffold-repro.zip).
The workspace:
```
Cargo.toml # [workspace] members = apps/my-app, packages/my-lib
apps/my-app/ # binary crate (has src/main.rs), depends on my-lib
packages/my-lib/ # library-only: src/lib.rs, NO src/main.rs, no [[bin]]
```
Cargo-only (no docker/moon needed) — run `./reproduce.sh`, or by hand:
1. `cargo build --package my-lib` → passes (library-only crate, no stub).
2. Write an empty `packages/my-lib/src/main.rs` (exactly what the plugin does in the Configs phase):
`: > packages/my-lib/src/main.rs`
3. `cargo build --package my-lib` → fails with `error[E0601]: main function not found`.
4. Add `autobins = false` under `[package]` in `packages/my-lib/Cargo.toml` → builds again.
Full docker flow (equivalent, via moon) — `docker build -f Dockerfile .`:
1. `moon docker scaffold my-app` — Configs phase writes the empty `src/main.rs` (and `src/lib.rs`) into `.moon/docker/configs/packages/my-lib/src/`.
2. `COPY --from=skeleton /app/.moon/docker/configs .` then `moon docker setup`.
3. `COPY --from=skeleton /app/.moon/docker/sources .` — overlays real sources (my-lib's real `lib.rs`); the empty `main.rs` from the configs skeleton is not pruned.
4. `moon run my-app:build` — its `deps: ['my-lib:build']` compiles my-lib's targets (including the phantom `bin`) and fails with the same E0601.
**Expected behavior**
After the `Sources` phase, each crate should contain exactly the files that exist in the
repository. A library-only crate should not carry a phantom `src/main.rs`, and the build should
compile it as a pure library.
**Screenshots**
N/A — error output included above.
**Environment**
- moon: 2.4.2
- Rust toolchain plugin: `rust_toolchain` v1.0.6 (resolved by moon 2.4.2)
- Affected crate type: Rust `layer: library`, library-only (`src/lib.rs`, no `src/main.rs`, no `[[bin]]`)
**Additional context**
Root cause: the empty `src/main.rs` placeholder written in the `Configs` phase is not pruned by
the `Sources` phase, and cargo's `autobins` treats it as a phantom binary target with no `fn main`.
Suggested fixes (any one resolves it):
1. Only scaffold the placeholder matching the crate's real target(s) (e.g. `src/lib.rs` for a
library) instead of unconditionally writing both `lib.rs` and `main.rs`.
2. Have the `Sources` phase prune skeleton placeholders that have no real source counterpart, so
each crate ends up byte-identical to the repo tree.
3. If both must be written during `Configs`, make the placeholder `main.rs` compilable
(`fn main() {}`) — though 1 or 2 are preferable, since a leaked phantom binary target is
surprising regardless of whether it compiles.
Current workaround: set `autobins = false` on each library crate's `[package]` (explicit `[[bin]]`
targets still build). This treats the symptom rather than the scaffold behavior.
Note: the fix likely belongs in `github.com/moonrepo/plugins` (`toolchains/rust`), since that is
where the placeholder is written; filing here as the plugins repo has no separate issue tracker/template.
2 条评论