Pre-RFC: Add macros target to Cargo manifest

One major issue with not being able to specify separate dependencies between proc-macros and the library crate is that you will end up building very big crates like syn twice, once for the target and once for the host.

You will also have the existing issue with target/host features being mixed continued on. AFAIK with the new resolver target/host features can be fully disambiguated (by either a marker on the edge with dependencies vs build-dependencies or a marker on a node with proc-macro = true), but if they are mixed into a single package's dependencies and shared between a normal crate and a proc-macro = true crate you lose that disambiguation.

5 Likes

Couldn't macros be handled similarly to tests, benchmarks and examples? Then we could simply add the macros to a macros folder in the root directory, and add dependencies to the [dev-dependencies] section.

2 Likes

I like the concept of this in general.

I would prefer to be able to group macros together in modules, rather than naming each individual macro. Perhaps it would make sense to have [[macros]] name a macro module (which may contain multiple macros), rather than an individual macro? If someone wants to break their macros up into one macro per module, they still can.

After an edition, we could potentially start inferring this from directory structure (as we do with bins).

1 Like

I think it'd be equally bad to mix it up with dev-dependencies as it would be to mix it up with regular dependencies, for the opposite reason. Mainly, right now crates can have all sorts of stuff in dev-dependencies that don't get compiled for their consumers, and thus don't matter much. But if macro dependencies were to be included there, then anyone depending on the crate would have to compile all of dev-dependencies, and that'd be a lot more test/benchmark/etc. crates which don't actually need to be compiled for the final product.

1 Like

One idea might be to mix proc-macro dependencies into build-dependencies, I'm not sure if the new resolver does this with existing proc-macros, or if it has separate "build-host" and "proc-macro-host" dependency sets, but I can't think of any downsides of mixing these together other than maybe confusion about the naming and interaction with the optionality of macros.

2 Likes

Or, we could just introduce [macro-dependencies] with no implied semantics wrt existing types of dependencies: they're dependencies for macros, no more, no less. I'll argue that that would be conceptually a bit cleaner, too.

5 Likes

I think macro targets should work exactly like build scripts, except they are compiled as a proc macro library instead of an executable, and exposed as a dependency to the main targets instead of run before building the targets.

To be concrete about what I mean:

  • Each package can have exactly one macros target, not many. There is a default path to find it at, which can be overriden with a packages.macros keys.
  • These packages are exposed to the library and binary targets as a dependency under a standard name (probably macros::). Users can rexport from this crate to expose them to downstream users. No magic re-export is done.
  • The macros target either has access to build-dependencies or to a new macros-dependencies section which functions from a resolution perspective just like build-dependencies except that one is exposed to the build script and one is exposed to the macros.

By making macros function just like build scripts except exposed as a library instead of executed before building, this is a reasonably scoped feature to add. But adding a new variadic target with its own configuration, sets of dependencies, etc, and adding new magic paths that have to be coordinated between cargo and rustc, greatly increase the scope of this feature for implementation & design. We should start with the easy version and can consider adding these bells and whistles later.

25 Likes

A possible solution is to give the macros a separate version, so you can increment the crate version without incrementing the macro version, for example:

[package]
name = "my_library"
version = "0.2.0"
edition = "2018"

[dependencies]
# dependency `my_macros` is implied

[features]
default = ["my_macros"]

[macros]
name = "my_macros"  # can be imported with `use my_macros::*`
version = "0.1.0"
path = "./macros"   # macros should be exported from `./macros/lib.rs`
optional = true     # this is an optional dependency

[macros.dependencies]
syn = "1"

Thank you everyone for your interest in this feature! Going through the feedback I'm inclined to agree that it should be a single target, and that dependencies should be specified with [macro-dependencies]. At least in cargo, you can achieve selectively enabled macros with feature flags.

So that I'm clear, a hypothetical serde import with this syntax would look like the following? If so I much prefer that. I believe “macros” isn’t currently reserved, though I’ll admit I don’t know if it needs to be to work.

use macros::serde::{Serialize, Deserialize};
use macro::serde::{Serialize, Deserialize};

No. The serde crate would have a new dependencies called macros. If it wants to expose those macros to other crates, it contains the line:

pub use macros::{Serialize, Deserialize};

And other users can now use serde::Serialize. But no one but serde can directly access serde's macro crate.

I suggested macros because its not a keyword; absolutely no change to rustc is necessary to implement this design. It's just cargo building another crate and then exposing it as a dep. It is, happily, a reserved name on crates.io, so there is and will be no macros crate in the public registry.

It's fine if someone who has a private dependency on a crate called macros can't use this feature until they rename that dependency, though we have to be careful not to break them either.

2 Likes

Want to explicitly mention that there are no IDE concerns about making proc-macros work roughly like build scripts.

5 Likes

Okay, I like that even more. I’ll update the top post in a bit with the all the feedback.

1 Like

Okay I’ve updated the top post with all the feedback.

The way I currently read the original post is that the macro doesn't have a particular version, as it's functionally private and only (potentially) exposed by the parent crate. Is that correct?

With regard to auto-enabling a feature flag, Given that it can already be done for other built-in crates, and this proposal otherwise behaves like a private built-in crate, I think that it makes sense to allow for a feature flag of the same name.

1 Like

I really like this Pre-RFC and am looking forward to simplify the amount of crate needed to be uploaded on crates.io. Is there any update on this, is this going to become a real RFC?

3 Likes

I don't currently have the energy to continue this, If someone else wants to take it forward feel free.

I'd be interested in pushing this forward along with others, as I don't have the time to do so singlehandedly. It's probably one of the better proposals out there, so hopefully others are interested.

5 Likes

Update: I've just forked the RFC repo and created a stub RFC file. Feel free to file issues & PRs. If you'd like commit access, just send me a message or file an issue.

As I previously stated, I don't have a ton of free time myself, but I can (hopefully) help facilitate the organization. Obviously the first thing that needs to be done is create a basic RFC-style write-up, which could likely be pulled in part from the OP here.

3 Likes

Putting this library here that allows you declare procedural macros inline in a crate.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.