When do crates need to be recompiled?

Suppose crateB depends on crateA.

Case 1: We change something in crateA that is visible based on crateA's pub interface.

Case 2: We change something in crateA that is INVISIBLE based on crateA's pub interface.

Are these cases handled differently by the incremental build process ? It seems some actions triggers a full rebuild of dependent crates. Other actions seems to trigger "fast rebuild" of dependent crates.

I am trying to understand the rules involved here.

1 Like

If any dependency changes you need to recompile all dependent crates. Rustc records the crate hash of all dependencies. This crate hash changes whenever the source code, any dependency or anything else that could affect the output changes. If you enable incremental compilation for a crate, it will do a lot less work when recompiling if there is no interface change of dependencies though.

1 Like

Can you point me at the docs for this? I am very interested in learning what "no interface change" and "a lot less work" means.

I don't think there are any docs for this.

Whichever part of the crate metadata was actually read by the crate currently being compiled. So for example function signatures for called functions or the MIR bodies for generic or #[inline] functions.

Things like codegen and typeck that are integrated with incremental compilation are skipped if everything they depend on is unchanged. The parser, macro expansion and many lints and linking (if necessary) are not skipped though.

2 Likes

I would say that conceptually it's no different from what Turbo Pascal 4.0 did 36 years ago: compiler just compares interface of your crate from how it looked in the previous compilation pass and how it looks now. If nothing that may affect output changed then there are no need to recompile crates which depend on freshly recompiled one.

What kind of docs you want to find? Internal compiler data structures that carry information about crate interface? These are unstable, obviously (and they were unstable in Turbo Pascal, too). The trick here is that you don't need any docs to apply that algorithm, if you have proper modules (and not just include files) then algorithm is trivial: after you have compiled interface for a crate you just need to use some kind of checksum to create an ID. Then you compare that ID to the ID which is stored in the other compiled crates. If there are difference then dependency have to be recompiled, if there are no difference then you may reuse previous version.

Note that in that process there are zero need to know about what exactly is stored in the compiled interface. It's enough to treat it as opaque binary blob.

1 Like

cargo build -vv will tell you exactly why each dependency changed according to Cargo. Sometimes it's only because filesystem timestamps are imprecise!

1 Like