feat(rustdoc): stabilize --emit flag rust-lang/rust#146220

Open

34 comments and reviews loaded in 1.93s

weihanglo Avatar

View all comments

---> FCP <---

Stabilization Report: rustdoc --emit

Feature: rustdoc --emit
Tracking issue: #83784
Stabilization PR: #146220

What we are stabilizing

This stabilizes the rustdoc --emit flag, which controls what types of output rustdoc produces. The flag accepts a comma-separated list of the following emit types:

  • html-static-files --- Shared static files with content-hashed filenames for safe caching.
  • html-non-static-files --- Per-crate documentation files with deterministic filenames.
  • dep-info[=<path>] --- A Makefile-compatible .d file listing all source files loaded during documentation generation. Same as rustc's dep-info files.

When --emit is not specified, the default behavior is --emit=html-static-files,html-non-static-files (i.e., full HTML documentation output, no dep-info).

What we are not stabilizing

  • Interaction between other unstable options, such as -Zrustdoc-mergeable-info and --output-format=doctest
  • Available options and the default options when --emit not specified.
  • Extension of per-type emit paths for options currently missing that.

Motivation

Cargo

The primary consumer is Cargo, which needs --emit=dep-info=<path> to precisely track the input dependencies of a rustdoc invocation (see the -Zrustdoc-depinfo unstable Cargo feature). Without dep-info, Cargo cannot detect changes to files pulled in via #[path = "..."] or similar mechanisms and leads to stale documentation in incremental builds.

Cargo also uses the selective emission mechanism (html-static-files / html-non-static-files) when the unstable -Zrustdoc-mergeable-info feature is active. It skips writing shared static files and search index during per-crate doc generation and defer them to a final merge phase.

Under stable usage, Cargo passes all three emit types together.

docs.rs

docs.rs is the other major consumer. It uses selective emission to avoid redundantly copying toolchain-wide static files for every crate, which has historically been a source of breakage. See the tracking #83784 and the about page for more https://docs.rs/about/download.

Tests

  • tests/run-make/emit-shared-files --- Verifies selective emission of static vs non-static files.
  • tests/run-make/rustdoc-dep-info --- Verifies dep-info generation, including explicit path and --out-dir interaction.
  • tests/run-make/rustdoc-scrape-examples-dep-info --- Verifies dep-info works with scrape-examples.
  • tests/run-make/rustdoc-default-output/ --- Verifies --help output shows [html-static-files,html-non-static-files,dep-info]
weihanglo Avatar

Questions:

  • Do we want to stabilize also unversioned-shared-resources option? It currently does nothing AFAIK.
  • Do we want a stabilization report?
  • How many details do we want to put in doc?
weihanglo Avatar

cc @aDotInTheVoid since you've involved in the PR removing the last blocker :)

fmease Avatar
src/doc/rustdoc/src/command-line-arguments.md · outdated
459 filename is `CRATE_NAME.d`. This emit type can can optionally be followed by
460 `=` to specify an explicit output path.
461 For example, `--emit=dep-info=/path/to/foo.d`.

Found a bug: rustdoc's --emit=dep-info=- doesn't output to STDOUT unlike rustc's.

Edit: This ought to be addressed in a separate PR by someone. I'll probably open an issue later to track this.

Opened #147649 (blocking the stabilization IMO).

I've updated the argument doc to include the --emit=depinfo=- fix

GuillaumeGomez Avatar

We discussed on today's rustdoc meeting. Before going any further, we will investigate exactly what each --emit option does exactly to have a clearer picture of what's actually needed and also to add missing documentation.

weihanglo Avatar

We discussed on today's rustdoc meeting. Before going any further, we will investigate exactly what each --emit option does exactly to have a clearer picture of what's actually needed and also to add missing documentation.

Thanks for it!

This PR contains a doc based on my understanding and the history of these options, but I am not 100% sure if that is correct

rustbot Avatar
rustbot on 2025-10-25 15:56:29 UTC · hidden as outdated
rustbot Avatar
rustbot on 2025-10-25 15:56:29 UTC · hidden as outdated
View on GitHub

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

fmease Avatar

I haven't read this yet but here are probably some very important and relevant considerations: #83784.

weihanglo Avatar

For the use case in Cargo, we actually don't need anything other than dep-info. The originak ask was here: #t-rustdoc > Plan to stabilize `--emit=dep-info[=path]` @ 💬:

One thing that might be a blocker: rust-lang/cargo#15605

Do we want to stabilize the --emit flag with all the emit types, or just dep-info? At this moment it is required for cargo to pass --emit=toolchain-shared-resources,invocation-specific,dep-info=<PATH> in order to make the generated doc look good and styled.

I guess one way forward is that rustdoc provides an extra emit type, say, default. So that cargo can run --emit=default,dep-info=/path/to/foo.d without even considering those other options. And in rustdoc documentation we can state that the default emit type is whatever the default emits rustdoc is doing. Pretty much like --remap-path-scope=all:

all - an alias for all of the above, also equivalent to supplying only --remap-path-prefix without --remap-path-scope.

notriddle Avatar

I've opened #148180, a follow-up that removes the no-op unversioned-shared-resources, which hasn't done anything ever since we switched to using hashes for cache busting.

rustbot Avatar
rustbot on 2025-10-27 18:49:33 UTC · hidden as outdated
rustbot Avatar
rustbot on 2025-10-27 18:49:33 UTC · hidden as outdated
View on GitHub

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

weihanglo Avatar
weihanglo left a comment · edited
View on GitHub

This PR has updated and removed the unversioned-shared-resources option, which was removed in #148180

View changes since this review

notriddle Avatar

I haven’t found any more problems with this feature (that don’t involve its interaction with other unstable features).

I think it can be stabilized in its current form.

GuillaumeGomez Avatar
GuillaumeGomez on 2026-01-12 20:53:24 UTC · hidden as RESOLVED
View on GitHub

@rfcbot fcp merge

rust-rfcbot Avatar
rust-rfcbot on 2026-01-12 20:53:26 UTC · hidden as RESOLVED
View on GitHub

Error encounted:
Provided team `` is invalid

GuillaumeGomez Avatar
rust-rfcbot Avatar

Team member @GuillaumeGomez has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

yotamofek Avatar
src/doc/rustdoc/src/command-line-arguments.md · outdated
445 This flag controls the types of output by rustdoc. It accepts a comma-separated
446 list of values, and may be specified multiple times. The valid emit kinds are:
447
448 - `toolchain-shared-resources` --- Generates shared static files that their
Suggested change
- `toolchain-shared-resources` --- Generates shared static files that their
- `toolchain-shared-resources` --- Generates shared static files whose
src/doc/rustdoc/src/command-line-arguments.md · outdated
451 change if the toolchain version or their contents change, so it is safe to
452 cache them wit the `Cache-Control: immutable` directive.
Suggested change
change if the toolchain version or their contents change, so it is safe to
cache them wit the `Cache-Control: immutable` directive.
change if the toolchain version or their contents change, or with the `Cache-Control: immutable` directive.
src/doc/rustdoc/src/command-line-arguments.md · outdated
445 This flag controls the types of output by rustdoc. It accepts a comma-separated
446 list of values, and may be specified multiple times. The valid emit kinds are:
447
448 - `toolchain-shared-resources` --- Generates shared static files that their

Not sure whether these triple-dashes are considered standard :)

It's a mdbook stuff. Not sure about rustdoc but Cargo book uses that everywhere

https://rust-lang.github.io/mdBook/format/markdown.html#smart-punctuation

rustbot Avatar
rustbot on 2026-01-13 16:09:57 UTC · hidden as outdated
rustbot Avatar
rustbot on 2026-01-13 16:09:57 UTC · hidden as outdated
View on GitHub

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

fmease Avatar

@rfcbot reviewed

Do we want a stabilization report?

I'd like to have a tiny stabilization report, just a few sentences esp. around the motivation would be great. For example, it's unclear to me right now whether Cargo is only interested in --emit=dep-info or if it also cares about the other two options toolchain-shared-resources & invocation-specific or rather the mechanism they enable, namely suppressing the emission of invocation-specific artifacts (via --emit=toolchain-shared-resources (sic!)) and shared resources (via --emit=invocation-specific (sic!)). That'd be useful to know imo.

@rfcbot concern tiny-stab-report

I've noticed that --emit=depinfo (i.e., default path) doesn't put the *.d file into the outdir but instead in the CWD. rustc does put in in the CWD by default but that's because the default outdir is the CWD. However, for rustdoc it's doc/. If you give rustc an --outdir=<DIR> it does put the *.d file into <DIR> while rustdoc completely ignores --outdir=<DIR> for depinfo.

Could we please honor the outdir?

@rfcbot concern outdir-not-honored

(minor bikeshedding) Re. the naming of toolchain-shared-resources and invocation-specific, it feels slightly off to me. I'll retract my concern immediately if everyone else is fine with these terms.

E.g., seeing the word toolchain in the context of the rustdoc binary seems a bit "weird". The (often rustup) toolchain is the set of rustc+rustdoc+docs+other components, they live at different level of abstraction? version-specific-shared-resources feels more natural, maybe? Although at this point, I'd just go with shared-resources. Isn't it obvious that these shared resources aren't stable across versions? If we hypothetically had a "stable rustdoc ABI", stable-shared-resources would do the trick, wouldn't it?

Re. --emit=invocation-specific, we're emitting an adjective here. I know, this is pure bikeshedding at this point and a noun like invocation-specific-data is hardly better?

@rfcbot concern naming

Lastly, what should we do if the user passes (stable) --test or (unstable) --output-format=doctest? Or if the user passes a *.md Markdown file to rustdoc (stable)? At the moment, --emit=dep-info doesn't emit any *.d file in these three cases because they take completely different execution paths in rustdoc. I'm leaning towards rejecting --emit=dep-info in these cases or is that too strict / artificially restricted? Or do we want to follow rustc's example: Under --test --emit=dep-info it doesn't generate any (test) binary, it only emits a *.d file.

@rfcbot concern interaction-with-other-modes


The following isn't an official concern and only tangentially related. I'm wondering whether --output-format=doctests (#134529) should actually be --emit=doctests. I'd also like to CC the proposed --print (#151618, print requests like rustc) for visibility, too. They're distinct but quite similar (I guess print requests are completely non-normal execution and emissions "happen on the side" during normal execution but they can also make execution halt early?).

fmease Avatar
src/doc/rustdoc/src/command-line-arguments.md

(non-blocking, just some impl cleanups we should do at some point)

  1. Under --emit=dep-info and --emit=invocation-specific rustdoc still generates an empty <OUTDIR>/static.files/ directory.
  2. On unknown emission/output types rustdoc doesn't list all legal types in the error diagnostic unlike rustc.
notriddle Avatar

Re. the naming of toolchain-shared-resources and invocation-specific, it feels slightly off to me.

toolchain-shared-resources should probably be called static-files, because that's the name of the directory that it actually emits.

test-dingus % ls
lib.rs
test-dingus % rustdoc +nightly --emit=toolchain-shared-resources -Zunstable-options lib.rs
test-dingus % ls
doc	lib.rs
test-dingus % ls doc
static.files

Maybe the invocation-specific data should be called data-files? That one's harder to name, but we don't want to call it crate-files (because it might come from a standalone markdown file) and I think invocation-specific-files is needlessly complex for what it is.

fmease Avatar

@rfcbot resolve outdir-not-honored

Fixed in #153003. Thanks, notriddle!

fmease Avatar

@rfcbot resolve naming

Fixed in #153460. Thanks, notriddle! If you disagree with the new names that were chosen please raise a new pFCP concern!

GuillaumeGomez Avatar

So just needs an update for the docs @weihanglo then the PR seems ready to go!

fmease Avatar

As well as a tiny stabilization report, please :D (FCP concern tiny-stab-report). Moreover, FCP concern interaction-with-other-modes hasn't been resolved yet.

weihanglo Avatar

RE: concern interaction-with-other-modes

Lastly, what should we do if the user passes (stable) --test or (unstable) --output-format=doctest? Or if the user passes a *.md Markdown file to rustdoc (stable)? At the moment, --emit=dep-info doesn't emit any *.d file in these three cases because they take completely different execution paths in rustdoc. I'm leaning towards rejecting --emit=dep-info in these cases or is that too strict / artificially restricted? Or do we want to follow rustc's example: Under --test --emit=dep-info it doesn't generate any (test) binary, it only emits a *.d file.

I wonder if --output-format=doctest we could also have dep-info, so other build system can deteremine whether a rustdoc invocation needs to rerun. For markdown input, I am not familiar with the use case. Sorry.

(That said, those options are unstable still)

rustbot Avatar
rustbot on 2026-03-14 15:02:25 UTC · hidden as outdated
rustbot Avatar
rustbot on 2026-03-14 15:02:25 UTC · hidden as outdated
View on GitHub

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

rust-log-analyzer Avatar
rust-log-analyzer on 2026-03-14 15:09:07 UTC · hidden as outdated
View on GitHub

The job tidy failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
fmt: checked 6743 files
tidy check
tidy [rustdoc_json (src)]: `rustdoc-json-types` modified, checking format version
tidy: Skipping binary file check, read-only filesystem
tidy [style (src)]: /checkout/src/doc/rustdoc/src/command-line-arguments.md:463: trailing whitespace
tidy [style (src)]: FAIL
removing old virtual environment
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'venv'
creating virtual environment at '/checkout/obj/build/venv' using 'python3.10' and 'virtualenv'
Requirement already satisfied: pip in ./build/venv/lib/python3.10/site-packages (26.0.1)
linting python files
---
info: ES-Check: there were no ES version matching errors!  🎉
typechecking javascript files
tidy: The following check failed: style (src)
Bootstrap failed while executing `test src/tools/tidy tidyselftest --extra-checks=py,cpp,js,spellcheck`
Command `/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-tools-bin/rust-tidy --root-path=/checkout --cargo-path=/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo --output-dir=/checkout/obj/build --concurrency=4 --npm-path=/node/bin/yarn --ci=true --extra-checks=py,cpp,js,spellcheck` failed with exit code 1
Created at: src/bootstrap/src/core/build_steps/tool.rs:1618:23
Executed at: src/bootstrap/src/core/build_steps/test.rs:1387:29

Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:02:52
  local time: Sat Mar 14 15:08:54 UTC 2026
  network time: Sat, 14 Mar 2026 15:08:54 GMT
##[error]Process completed with exit code 1.
##[group]Run echo "disk usage:"
weihanglo Avatar

@fmease A informal stabilization report has got updated in the PR description. Feel free to edit :)

One thing I put there was my personal opinion

What we are not stabilizing

...

  • Available options and the default emit options.

I am trying to mimic --remap-path-scope that rustdoc can reserve the future flexibility to extend the default options. I am not sure if this is what rustdoc team wants though. If yes, I could update the doc with something like this:

The options accepted by --emit are not exhaustive — new options may be added in future releases. Newly introduced options may also be included in the default outputs when --emit is not specified.

ojeda Avatar

I wonder if --output-format=doctest we could also have dep-info, so other build system can deteremine whether a rustdoc invocation needs to rerun.

That would be nice!

Currently, I approximate it by rerunning if the object file changes, which in turn should cover the source files, but that of course means we require building the crate before.

So if you mean we could generate the tests independently (e.g. in parallel) to building (which seems to be the case from a quick test, i.e. the crate is not required to be built to extract the tests), then it would quite nice!

notriddle Avatar

It seems like we can avoid going through a one-way door by making it a fatal error to use --test and --emit at the same time. #153895

fmease Avatar

@rfcbot resolve interaction-with-other-modes

As already mentioned, we now emit an error if --emit and --test are specified at the same time (PR #153895). Moreover, --emit=depinfo now works for Markdown input files (PR #154352).

--emit=html-static-files, --emit=dep-info still emits a HTML artifact for Markdown input files despite --emit not containing html-non-static-files if the flag is specified like that. That's tracked in #155298 (ideally it would be fixed before the stabilization but it's not super pressing).

We still don't honor --emit under --output-format doctests -Zunstable-options but it's okay to postpone that.

@rfcbot resolve tiny-stab-report

Thanks, weihanglo, for adding a stabilization report!

notriddle Avatar
rustbot Avatar

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.