Sophisticated Code Generation with Cargo

I want to do some sophisticated code generation within a Cargo project, but I'm unsure how to do this. I basically want to have a crate which generates code from an XML specification. To generate this code, the generator requires some options.

@brendanzab's gl-rs project seems to do something very similar, the build script generates bindings to the OpenGL library depending on some options. This code just seems to be spitted into the crate however. I would like to have the generated code by in a seperate crate, so I can make it a dynamic library, and when other crates depend on it, I don't have to regenerate and rebuild it for every crate.

The issue I am facing is that when I generate the code inside a seperate package, I cannot pass options anymore regarding how to build it. When I generate the code inside the package using the code, it'll always be statically linked, and I cannot make it a dynamic library anymore.

Does anyone have any ideas on how to get the best of both worlds, meaning having the generated code rest in a seperate crate and pass options to the code generator.

1 Like

Not a cargo expert, and this will depend on the kind of configs you want, but you can potentially use "features". This will allow each project to customize the build. The issue then becomes how do you deduplicate identical feature builds. I believe there are longterm plans to have cargo cache dependency builds between different projects. Such a thing would necessarily have to be feature-aware. So if that gets implemented you should be golden? I think?

2 Likes

I've looked into it, but that seems primarily for package dependencies, and not so much for simply configuration options. I'm unsure if I can hack something to use it as configuration options. Thanks though. :slight_smile:

I'm not sure I understand your problem. Can't you generate a crate once and build it in both static and dynamic forms? Or does the output need to vary on the API level based on input options (which doesn't seem like a good idea to me)?

P.S. I'm going to implement a code generator for GObject introspection bindings, so my problems are going to be similar.

1 Like

The code and partially the API indeed depend on some input options.This is required because otherwise the library would become gigantic and the user would have to use function pointers all the time which messes a bit with the safety I am trying to provide. (not to forget it'd be a pain to 'unwrap' each function from an Option type)

The user should be able to pass input regarding what functionality it expects and how it should be implemented, so the generator can get rid of unneeded functions and implement the needed functions in a way customised for the user.

1 Like

The size of a library may be a problem for compilation time and for storage, but it's hardly a performance problem. From a static crate, only those items are linked which are used. A dynamic library is memory-mapped as usual.

I didn't get why would you use function pointers out of concern for image size. Add-on functionality conforming to a common API may be provided via crates with trait implementations and using object type pointers for dynamic dispatch.

I want to be able to make it a dynamic library because that makes it easier to update. I don't want to have to update every component just when one component does. In this case the size of the library does matter. It's not so much a performance issue.

Also internal use of function pointers is a requirement, the functions need to be initialised out of safety concerns.

I'm afraid that churn in Rust ABIs would not make updating dynamic crates very practical in the 1.0 timeframe.

For my project, I plan to keep 1-to-1 correspondence between units of the interface specification (i.e. GIR namespaces) and output crates. I haven't come up with a reason to make it any more sophisticated.

Why'd it not practical? The ABI should be stable in 1.0?

The abi should be stable in 1.0

There is no guarantee of ABI stability any time soon.