Skip to content

feat(rustdoc): stabilize --emit flag#146220

Open
weihanglo wants to merge 1 commit intorust-lang:mainfrom
weihanglo:rustdoc-emit
Open

feat(rustdoc): stabilize --emit flag#146220
weihanglo wants to merge 1 commit intorust-lang:mainfrom
weihanglo:rustdoc-emit

Conversation

@weihanglo
Copy link
Member

@weihanglo weihanglo commented Sep 5, 2025

View all comments

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]

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. labels Sep 5, 2025
@weihanglo
Copy link
Member Author

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
Copy link
Member Author

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

@weihanglo weihanglo marked this pull request as ready for review September 24, 2025 19:21
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 24, 2025
@fmease fmease added the needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. label Oct 13, 2025
@notriddle notriddle self-assigned this Oct 13, 2025
@GuillaumeGomez
Copy link
Member

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
Copy link
Member Author

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

This comment has been minimized.

@fmease
Copy link
Member

fmease commented Oct 25, 2025

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

@weihanglo
Copy link
Member Author

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 added a commit to notriddle/rust that referenced this pull request Oct 27, 2025
This option hasn't done anything for a long time, and can be
removed. I've kept a shim in place to avoid breaking docs.rs,
but the option no longer does anything.

Using git-blame, I tracked this option down to
f77ebd4, the commit that
introduced EmitType in the first place. It was used with
SharedResource::Unversioned, which no longer exists since
f9e1f6f removed them.

Part of rust-lang#146220
@notriddle
Copy link
Contributor

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.

notriddle added a commit to notriddle/rust that referenced this pull request Oct 27, 2025
This option hasn't done anything for a long time, and can be
removed. I've kept a shim in place to avoid breaking docs.rs,
but the option no longer does anything.

Using git-blame, I tracked this option down to
f77ebd4, the commit that
introduced EmitType in the first place. It was used with
SharedResource::Unversioned, which no longer exists since
f9e1f6f removed them.

CC rust-lang#146220
Part of rust-lang#83784
@rustbot

This comment has been minimized.

notriddle added a commit to notriddle/rust that referenced this pull request Oct 28, 2025
This option hasn't done anything for a long time, and can be
removed. I've kept a shim in place to avoid breaking docs.rs,
but the option no longer does anything.

Using git-blame, I tracked this option down to
f77ebd4, the commit that
introduced EmitType in the first place. It was used with
SharedResource::Unversioned, which no longer exists since
f9e1f6f removed them.

CC rust-lang#146220
Part of rust-lang#83784
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Oct 28, 2025
…rces, r=GuillaumeGomez

rustdoc: remove `--emit=unversioned-shared-resources`

This option hasn't done anything for a long time, and can be removed. I've kept a shim in place to avoid breaking docs.rs, but the option no longer does anything.

Using git-blame, I tracked this option down to
f77ebd4, the commit that introduced EmitType in the first place. It was used with SharedResource::Unversioned, which no longer exists since f9e1f6f removed them.

CC rust-lang#146220
Part of rust-lang#83784
This flag controls the types of output by rustdoc. It accepts a comma-separated
list of values, and may be specified multiple times. The valid emit kinds are:

- `toolchain-shared-resources` --- Generates shared static files that their
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

This comment has been minimized.

@fmease
Copy link
Member

fmease commented Feb 20, 2026

@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?).

Copy link
Member

@fmease fmease Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(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
Copy link
Contributor

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.

JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Mar 2, 2026
rustdoc: make `--emit` and `--out-dir` mimic rustc

The behavior in the test case matches rustc's:

    test-dingus % ls
    main.rs
    test-dingus % mkdir foobar
    test-dingus % rustc --emit=dep-info main.rs --out-dir=foobar
    test-dingus % ls
    foobar  main.rs
    test-dingus % ls foobar
    main.d
    test-dingus % rustc --emit=dep-info=testfile.d main.rs --out-dir=foobar
    test-dingus % ls
    foobar          main.rs         testfile.d
    test-dingus % ls foobar
    main.d

CC rust-lang#146220 (comment)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Mar 2, 2026
rustdoc: make `--emit` and `--out-dir` mimic rustc

The behavior in the test case matches rustc's:

    test-dingus % ls
    main.rs
    test-dingus % mkdir foobar
    test-dingus % rustc --emit=dep-info main.rs --out-dir=foobar
    test-dingus % ls
    foobar  main.rs
    test-dingus % ls foobar
    main.d
    test-dingus % rustc --emit=dep-info=testfile.d main.rs --out-dir=foobar
    test-dingus % ls
    foobar          main.rs         testfile.d
    test-dingus % ls foobar
    main.d

CC rust-lang#146220 (comment)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Mar 3, 2026
rustdoc: make `--emit` and `--out-dir` mimic rustc

The behavior in the test case matches rustc's:

    test-dingus % ls
    main.rs
    test-dingus % mkdir foobar
    test-dingus % rustc --emit=dep-info main.rs --out-dir=foobar
    test-dingus % ls
    foobar  main.rs
    test-dingus % ls foobar
    main.d
    test-dingus % rustc --emit=dep-info=testfile.d main.rs --out-dir=foobar
    test-dingus % ls
    foobar          main.rs         testfile.d
    test-dingus % ls foobar
    main.d

CC rust-lang#146220 (comment)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Mar 3, 2026
rustdoc: make `--emit` and `--out-dir` mimic rustc

The behavior in the test case matches rustc's:

    test-dingus % ls
    main.rs
    test-dingus % mkdir foobar
    test-dingus % rustc --emit=dep-info main.rs --out-dir=foobar
    test-dingus % ls
    foobar  main.rs
    test-dingus % ls foobar
    main.d
    test-dingus % rustc --emit=dep-info=testfile.d main.rs --out-dir=foobar
    test-dingus % ls
    foobar          main.rs         testfile.d
    test-dingus % ls foobar
    main.d

CC rust-lang#146220 (comment)
rust-timer added a commit that referenced this pull request Mar 3, 2026
Rollup merge of #153003 - notriddle:emit-outdir, r=fmease

rustdoc: make `--emit` and `--out-dir` mimic rustc

The behavior in the test case matches rustc's:

    test-dingus % ls
    main.rs
    test-dingus % mkdir foobar
    test-dingus % rustc --emit=dep-info main.rs --out-dir=foobar
    test-dingus % ls
    foobar  main.rs
    test-dingus % ls foobar
    main.d
    test-dingus % rustc --emit=dep-info=testfile.d main.rs --out-dir=foobar
    test-dingus % ls
    foobar          main.rs         testfile.d
    test-dingus % ls foobar
    main.d

CC #146220 (comment)
@fmease
Copy link
Member

fmease commented Mar 4, 2026

@rfcbot resolve outdir-not-honored

Fixed in #153003. Thanks, notriddle!

rust-bors bot pushed a commit that referenced this pull request Mar 11, 2026
rustdoc: rename `--emit` names

These new names are pithier and match up with the rest of our terminology:

- `--emit=html-static-files` matches the default name of the directory that it actually emits, which is `static.files` (the hyphen is used for emit because every other emit option uses hyphens, but the directory uses a dot because we don't want its name to conflict with a crate).
- `--emit=html-non-static-files` matches the convention that emit is a noun, not an adjective, and it logically groups with other data formats.

This commit changes the docs, but leaves in support for the old names, to break the cycle with cargo and docs.rs. This commit needs merged, then cargo and docs.rs will be updated to use the new names, then, finally, the old names will be removed.

CC #146220 (comment)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Mar 11, 2026
rustdoc: rename `--emit` names

These new names are pithier and match up with the rest of our terminology:

- `--emit=html-static-files` matches the default name of the directory that it actually emits, which is `static.files` (the hyphen is used for emit because every other emit option uses hyphens, but the directory uses a dot because we don't want its name to conflict with a crate).
- `--emit=html-non-static-files` matches the convention that emit is a noun, not an adjective, and it logically groups with other data formats.

This commit changes the docs, but leaves in support for the old names, to break the cycle with cargo and docs.rs. This commit needs merged, then cargo and docs.rs will be updated to use the new names, then, finally, the old names will be removed.

CC rust-lang#146220 (comment)
@fmease
Copy link
Member

fmease commented Mar 11, 2026

@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
Copy link
Member

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

@fmease
Copy link
Member

fmease commented Mar 11, 2026

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
Copy link
Member Author

weihanglo commented Mar 14, 2026

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
Copy link
Collaborator

rustbot commented Mar 14, 2026

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

This comment has been minimized.

@weihanglo
Copy link
Member Author

@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
Copy link
Contributor

ojeda commented Mar 14, 2026

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
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants