Acceptable for procedural macros to _write_ outside of source file?

#[gen_proto]
pub struct Foo {
...

Here is the thing: I don't want gen_proto to generate any code inline. Instead, I want it to write to proj/src/module_path/Foo.proto

Is such behavior acceptable for procedural macros, or does it break unwritten rules / assumptions ?

When building a crate you aren't allowed to write to the source directory. Cargo will refuse to upload a crate to crates.io if you attempt to do this. You are only allowed to touch OUT_DIR and even then you should probably only do this from a build script and not a proc macro.

Would it be possible to instead generate the rust struct from the protobuf specification?

1 Like

There are two usual approaches to this problem:

  • The simpler / faster approach is to have the #[gen_proto] generate hidden functions which are able to generate your desired stuff (usually behind a feature gate). Such functions are collected (either through life-before-main or through linker tricks) into a "compile-time" collection. You then have a special unit test that iterates through that collection and calls all those functions, with some given out path, effectively generating the desired file.

    This is the approach currently taken by ::safer_ffi, for instance:

    safer_ffi::headers - Rust

  • The more proper approach would be to write a CLI kind of application (tip: you could try to use GitHub - trailofbits/dylint: A tool for running Rust lints from dynamic libraries to hook onto lint capabilities to get the code exploration done for you) which traverses your code looking for the attribute / the marker, and have that tool be the one responsible for generating the files. This is what wasm_bindgen does, for instance.

1 Like

Noob question: What is the advantage of hooking into dylint, compared to hooking into proc_macro::TokenStream (i.e. fake being a procedural macro) ?

If I understand correctly, dylint is a separate binary, run independently of the compiler invocation, and so it is correct for it to do more.

1 Like

Put another way, if I am running a separate program outside of the Rustc compilation process, I need some way to to extract a TokenStream for everything happening after my proc macro call.

dylint is one approach to get this ?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.