Incremental Compilation
WARNING
Procedural macros, by design, introduce a lot of overhead during the compilation. They may also be harder to maintain. Prefer the declarative inline macros written directly in Cairo, unless you have a specific reason to use procedural macros. There are several reasons for this:
- Compilation overhead: procedural macros are Rust crates compiled into shared libraries, adding a full Rust compilation step (via Cargo) on top of the Cairo build and significantly increasing build times.
- Rust toolchain dependency: anyone using your macro must have the Rust toolchain (Cargo) installed on their machine (unless the macro is distributed as a precompiled shared library, which is not always the case).
- Harder to debug: errors in macro expansion surface as confusing Cairo compiler diagnostics, making them difficult to diagnose.
- Higher maintenance burden: they require knowledge of both Rust and Cairo, and the FFI boundary between them adds complexity.
Please see the declarative macros chapter in Cairo Book for more information.
INFO
To use procedural macros, you need to have Rust toolchain (Cargo) installed on your machine. Please see Rust installation guide for more information.
Macros and incremental compilation: invalidating caches with fingerprints
Scarb implements incremental caching, which means that subsequent builds can be sped up with use of caches produced during former builds.
This is possible because the relation between Cairo code and produced artifacts is deterministic. During the compilation we can save some state of the compiler at some point in time and then load it in another run from disk and continue, as if we never stopped compiling.
As procedural macros can inject additional logic defined by the macro author, it needs to uphold the same determinism assumptions as the compiler itself.
WARNING
This means that all macro outputs should be deterministic in regard to the macro input passed by Scarb (i.e. the token stream the macro implementation receives as an argument).
If your macro needs to read inputs from other sources that Scarb is not aware of, say from environmental variables, you need to define a fingerprint for this input with the fingerprint attribute from procedural macro API. Fingerprint is a function that returns a single u64 value. If the value changes, Scarb will invalidate incremental caches for code depending on this macro. This enables the macro author to manually invalidate caches based on external inputs. Usually, this is simply a hash of the input (note that you need to use a stable hash function, like xxh3, not rng-seeded ones, like the default hasher used in Rust).