Skip to content

--emit=metadata emitting empty .rmeta file #67293

Description

@0dvictor

At present, rustc --emit=metadata foo.rs generates an empty rmeta file named libfoo.rmeta. The logic behind is that rmeta is not needed for certain crate types. However, I feel we should still save it to a file when user explicitly requested it by --emit=metadata, especially the metadata is always available in memory regardless of output crate types.

Activity

  1. changed the title [-]`--emit metadata` emitting empty .rmeta file[/-] [+]`--emit=metadata` emitting empty .rmeta file[/+] on Dec 14, 2019
  2. added
    A-metadataArea: Crate metadata
    C-bugCategory: This is a bug.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Dec 14, 2019
  3. ehuss commented on Dec 14, 2019

    @ehuss
    Contributor

    I would be cautious with this, as it could be a noticeable increase in disk usage for some projects. Rust already uses too much disk space. There should probably be a stronger reason than "it seems like it should".

  4. bjorn3 commented on Dec 14, 2019

    @bjorn3
    Member

    Emitting an empty .rmeta file makes sense, as the metadata is what is needed to use it with --extern/extern crate. For executables, cdylibs and staticlibs that is impossible, so no metadata is needed in those cases.

  5. 0dvictor commented on Dec 16, 2019

    @0dvictor
    Author

    There should probably be a stronger reason than "it seems like it should".

    Definitely. IMO, user's explicit request should be honored; therefore, in this case, the explicit request is ---emit=metadata.

    On the other side, I agree with @bjorn3 's point that the metadata is not needed for certain crate types; however, under this scenario, I feel rustc should print an error or warning message instead of silently generating an empty file.

  6. 0dvictor commented on Dec 16, 2019

    @0dvictor
    Author

    it could be a noticeable increase in disk usage for some projects

    Does it mean these projects request --emit=metadata with crate types that do not need metadata? If it does, is there any obstacles or restrictions that these project cannot remove --emit=metadata from the arguments?

  7. bjorn3 commented on Dec 16, 2019

    @bjorn3
    Member

    Does cargo check use --emit=metadata for executables?

  8. ehuss commented on Dec 16, 2019

    @ehuss
    Contributor

    Does it mean these projects request --emit=metadata with crate types that do not need metadata?

    Yes. this is how cargo check works.

  9. jyn514 commented on May 24, 2023

    @jyn514
    Member

    Why is cargo passing --emit=metadata for these crate types? It knows already that it's not passing crate-type=lib, it can omit the metadata flag in that case.

  10. cosmicexplorer commented on Dec 29, 2025

    @cosmicexplorer

    @ehuss

    I would be cautious with this, as it could be a noticeable increase in disk usage for some projects. Rust already uses too much disk space. There should probably be a stronger reason than "it seems like it should".

    cargo uses too much disk space, because cargo does not have any transitive checksum-based invalidation model employing something like LRU gc, so cargo is for example unable to avoid repeatedly regenerating said .rmeta file. the proposal in OP is the one single thing i would need from rustc in order to implement the kind of extremely powerful cached and pipelined build process we implemented for scala compiles with pants bazelbuild/rules_rust#428 (comment)

  11. bjorn3 commented on Dec 29, 2025

    @bjorn3
    Member

    Cargo always keeps the -Cmetadata and -Cextra-filename arguments in sync, so if the output rmeta can possibly be identical to an existing rmeta (which requires -Cmetadata to be identical due to being part of the ABI), then the output filename will be identical (because the crate name and -Cextra-filename are identical) and thus the new rmeta file will overwrite the old rmeta file. Checksum-based invalidation can only make builds faster, not cause them to use less disk space.

  12. cosmicexplorer commented on Dec 29, 2025

    @cosmicexplorer

    Checksum-based invalidation can only make builds faster, not cause them to use less disk space.

    I used "invalidation" too loosely there, sorry. pants does not compute what I call "negative invalidation" like cargo. Build tasks are pure functions which may be async https://www.pantsbuild.org/stable/docs/writing-plugins/the-rules-api/concepts--this results in what I would call "positive invalidation", which is how we can express recursive task dependencies that still terminate. We also completely virtualize file operations https://www.pantsbuild.org/stable/docs/writing-plugins/the-rules-api/file-system.

    Checksum-based invalidation indeed does not improve anything (I believe an old pants contributor proposed exactly this to cargo recently; sorry about that). But checksum-based data stores can be used to meaningfully deduplicate results--not just file content, but also shared directories, e.g. if you use a merkle tree encoding.

    More generally, a content-addressed store/CAS allows you to manipulate data in the programming language without reference to the filesystem at all. If you can trust that the checksum can be dereferenced from the CAS, then you can apply all sorts of heuristics and whatnot (e.g. materializing extremely common files as symlinks) instead of having to constantly translate your logic through whatever the filesystem is able to express.

    A merkle tree can also be transferred across a network safely, while optimistically checking for the existence of a checksum in any such remote node can still be very quick.

    I do have some qualms with the pants fs API, which does not allow the task developer to apply any sort of e.g. string tag to contextualize what this arbitrary merkle node represents to the user.

  13. cosmicexplorer commented on Dec 29, 2025

    @cosmicexplorer

    My initial comment was extremely hastily written--I was frustrated because file-level metadata from @xeno-by (now @burmako)'s https://github.com/twitter/rsc was how @winw and I were able to productionize what Eugene calls "outline compilation" in his great talk here https://www.youtube.com/watch?app=desktop&v=8SnIBkJXD8I. However one thing Olafur and Eugene repeatedly described to me is how Odersky's metadata formats like TASTy (https://docs.scala-lang.org/scala3/guides/tasty-overview.html) were not really designed for efficient querying, and require many recursive dereferencing operations, whereas Eugene's SemanticDB is denormalized beforehand https://scalameta.org/docs/semanticdb/specification.html.

    Especially consider the framing at https://scalameta.org/docs/semanticdb/guide.html#semanticdb-based-tools, which notes its independence from compiler internals. I think defining a public protocol in terms of denormalized flat records which rustc (or something else) could compile a .rmeta to could be a way to achieve the success we found at Twitter making use of Eugene's work.

  14. cosmicexplorer commented on Dec 30, 2025

    @cosmicexplorer

    Reading about the query-based architecture of the rust compiler just now and it being motivated by incrementality and am very surprised to realize how strong the analogy is to the pants v2 engine (https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html#the-try-mark-green-algorithm).

    @jyn514 for example the bootstrapping "mini pants" task system I was pondering for a prototype to unify bootstrap and cargo would have been making extremely similar tradeoffs as described for the rustc incremental compilation system https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation-in-detail.html#query-modifiers and since I want it to be cute (unlike pants) I was absolutely going to have an explicitly-invoked content-addressed store in the filesystem.

    (& btw If anyone knows offhand where I should look to learn more about this incrementality persistence mechanism plz lmk https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation-in-detail.html#incrementality-of-on-disk-data-structures)

    zip-based multiple crate archive format proposal

    But the really important thing I wanted to mention was that reading enough of @bjorn3's posts today caused me to feel like I had an epiphany of sorts but maybe not:

    (a) we (often) actually lose information when we generate the system-native archive format, particularly the ability to split back out into unambiguous record boundaries
    @bjorn3 #73632 (comment)

    By the way I forgot if I asked this already, but would a rustc command to bundle multiple rlibs into a single staticlib without requiring any rust source for this staticlib work? So something like rustc --crate-type staticlib -Zlink-libs liba.rlib libb.rlib -o libcombined.a. This should allow diamond dependencies I think. You could run it just before invoking the system linker.

    (b) in addition to crate-specific data/config, there is global data/config we need before we can generate an output binary. it seems that we lose the ability to modify that information once we bounce to the system-native format
    @bjorn3 #73632 (comment)

    We already have the staticlib crate type for bundling everything into a single .a file. Turning individual rlibs into .a files won't work as it would either duplicate the allocator shim and such between every such .a or or omit them from all and produce unlinkable .a files. One idea I have is to produce a new crate type which is to staticlib as dylib is to cdylib. It would act like a dylib with respect to producing the allocator shim and bundling multiple crates, but produce a .a file instead of a .so file and add the necessary crate metadata to allow consuming it like a regular crate from rustc. See also the end of #111594 (comment)

    (c) the "crate type" seems ridiculously overloaded and it seems confusing by itself to me that it can represent a single (?) compiled crate or like multiple
    @bjorn3 #111594 (comment)

    A couple of days ago my PR #106560 got merged. By combining -Z staticlib-allow-rdylib-deps with -Z staticlib-prefer-dynamic, libstd will be dynamically linked and --print native-static-libs will tell you how to dynamically link against libstd. This does not however enable statically linking libstd. I'm not sure what the best way to add that is though.

    The staticlib just like cdylib is not supposed to export any symbols outside of those that are #[no_mangle], which would make libstd.a impossible. Currently it does export all symbols as there is no way to indicate that a symbol shouldn't be exported from an archive (for cdylib we have control over the linker, for staticlib we don't). This needs to be fixed to allow multiple rust staticlib's to be linked into the same executable or dynamic library without getting symbol conflicts. (#104707) Maybe one option would be to have something like a rstaticlib crate type which can bundle multiple crates and can be statically linked (that is it is to staticlib what dylib is to cdylib) and then additionally ship libstd as rstaticlib.

    & then this part:

    Maybe one option would be to have something like a rstaticlib crate type which can bundle multiple crates

    affordances of a zip-based CAS archive

    I am not an expert in backend codegen, but I have spent immense amounts of time with the zip file format (the zip crate will never have a CVE on my watch but we do have SIMD parsing because of me) & with packaging software. In python, we have distributions (which I analogize to the current set of rust crate types), but we also have zipapps, which are themselves actually much less structured than pex files.

    Also important: PEP 777 is the wheel deal (and Emma Smith who is authoring it is also the author of the Rust PEP and she rocks). See my infodump in the PEP thread.

    Some final points:

    1. both java and python allow loading code from a zip file. for various reasons, this can sometimes but not always be significantly faster than traversing the filesystem, particularly in the case of generated module paths with many many entries.
      • it also means you can actually be 100% confident about where your code is being loaded from (death to linear path traversal!!!!)
    2. so rust already has the crate hash. python's analogy would be the direct url. please consider the following:
    3. so i need to actually implement this, but zstd dicts can be used to get faster-than-light compression ratios by codifying context: https://codeberg.org/cosmicexplorer/corporeal
    4. it's kind of a gimmick but you can always just append to the end of a zip file's local entries (before the central directory) and this makes it incredibly useful for incremental accumulation of structured persistent data
    5. astral blatantly steals credit for this technique i invented too but basically zip archives can be introspected even over a network A pip resolve command to convert to transitive == requirements very fast by scanning wheels for static dependency info (WORKING PROTOTYPE!) pypa/pip#7819

    _hacky_extract_sub_reqs() (see https://github.com/cosmicexplorer/pip/blob/a60a3977e929cfaed6d64b0c9e3713d7c502e51e/src/pip/_internal/resolution/legacy/resolver.py#L550-L552) will:
    a. send a HEAD request to get the length of the zip file
    b. perform several successive GET requests to extract the relative location of the METADATA file
    c. extract the DEFLATE-compressed METADATA file and INFLATE it
    d. parse all Requires-Dist lines in METADATA for requirement strings

    @bjorn3:

    Furthermore there may be performance benefits to using different archive formats
    this is rust!!!!! we didn't just stick with null terminated strings because they were "system native"!!!!!

    at a basic level, differentiating "flexible archive format we can extend for our own purposes" seems SUPER important to differentiate from "system-native archive format we can provide to a C linker".

    i unfortunately have absolutely no clue what ar or ld does to generate a .a archive (yet), but i am absolutely 10000% sure that generating a zip-based rstaticlib absolutely can be pipelined, and that a zip is much more structured than most other types of formats i can name

    oh and i'm greedy too this means my rust build system can incorporate a concept of ABI and target compatibility too

    @bjorn3 #67293 (comment)

    Emitting an empty .rmeta file makes sense, as the metadata is what is needed to use it with --extern/extern crate. For executables, cdylibs and staticlibs that is impossible, so no metadata is needed in those cases.

    thanks for this explanation of those semantics! do i understand correctly, then, that your proposed new output type rstaticlib would be a way to e.g. enforce the emission of a .rmeta while retaining backwards compat?

    thx for listening!! i will probably put this up as a pre-RFC on the discourse tomorrow!

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-metadataArea: Crate metadataT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions