How to override the module [#path] attribute

Hi all,

I'm using a crate for an embedded project that uses the module [#path] attribute to include an auto-generated Rust module. Something like:

// lib.rs
[#path = "../../../autogen.rs"]
pub mod autogen;

I'd like to be able to build this crate and override that path to the autogenerated code and can't find any way of doing this in the Cargo/Rust world. I'm used to a Makefile flow where you'd just set a make variable when running make and can't find a way of doing the same with Cargo.

I guess what I want is the following directory structure:

root
    libautogen/
        src/
            lib.rs
        Cargo.toml
    app1/
        autogen/
            autogen.rs
        src/
            main.rs
        Cargo.toml
    app2/
        autogen/
            autogen.rs
        src/
            main.rs
        Cargo.toml

So app1 and app2 are for slightly different embedded targets and autogen.rs contains hardware specific code that is included in libautogen.

Is there any way of doing this? Or perhaps I just need to modify the library to extract the autogenerated dependencies and put them in their own library that sits within each app? It might just be the fact that we're trying to do something the original authors didn't anticipate.

Cheers.

Under what conditions do you want the #[path] attribute to change? If it's depending on the target, that's usually done with conditional dependencies instead of modules. You usually don't want a file to be a module in two different crates.

You can't change the #[path] if you don't control the crate's source code. If you do, you can just set it to whatever you like.

1 Like

In this example the two applications would have slightly different HALs. So all the common HAL code lives in lib.rs and the application specific changes live in autogen.rs.

The directory structure I've given is actually probably not a good one for describing my problem. What I'm working with is more like the following structure where the build directories are transitive and all code is autogenerated by various flows.

root
    libautogen/
        src/
            lib.rs
        Cargo.toml
    app1/
        src/
            main.rs
        Cargo.toml
    app2/
        src/
            main.rs
        Cargo.toml
build1/
    autogen.rs
    other_build_artefacts
build2/
    autogen.rs
    other_build_artefacts

So I want to be able to build both Cargo applications for each of the autogenerated builds.

So 4 executables total? You could make a feature in libautogen for each autogen. Or you could make libautogen define a trait, implement that trait in each autogen, and then use the autogens as conditional dependencies.

Thanks and yes, in this case 4 executables, but those directories are transitory. Every time I rebuild my hardware, and this code might be used across multiple projects, the build directory path may change. So I can't hard code anything into the Rust libraries or applications. Hence asking how to override it. The original authors only had a single build directory so didn't care about this.

In a standard embedded C environment with Makefiles this is all dead easy. I create each build directory with a simple Makefile that includes the library, adds any custom source to the source list, and runs gcc. Is there no way to pass that path attribute as an environment variable or have a build script set it from a variable or pass a command line argument to Cargo that overrides it?

I don't think I really understand what you're trying to do. Normally autogenerated files are placed by build scripts relative to the OUT_DIR path (which is an environment variable set by Cargo), and included in the crate's source like this:

mod generated {
    include!(concat!(env!(OUT_DIR), "/autogen.rs");
}

Does it solve your issue?

1 Like

I'm trying not to touch the Rust app aside from point it at my autogen.rs which will be in different directories. Ideally I'd like to be able to build the apps in each of the build directories in parallel and run a bunch of jobs on the compiled apps in parallel. Think job scheduler and parallel execution on a cluster.

How are you detecting the path to the generated code? Is it an environment variable?

You can't make a hardcoded path point to a different path, so either the rust code has to change or you need to copy or link the generated code into the hardcoded path.

The parallel build thing is separate since cargo doesn't have any such functionality. That can be done by specifying a different --target-dir for each cargo call. If you use an environment variable to specify the path, then it's easy to configure each cargo call to use a different path.

Ok, thanks. That --target-dir helps a lot.

So if I'm going to modify the Rust source and remove the hard coded path, what might I do differently? If you were starting from scratch and had a bunch of library code that was fixed and a few HAL modules that changed with the target hardware, what's best practice?

This link from above is the standard way to generate code. You have a build script that writes the code into OUT_DIR and then you include! that in your source. The only thing you need to add is that you need to pass some per-cargo-invocation configuration into the build script, probably with environment variables.

If you don't want to change where the generated code is, you could put its entire path into an environment variable and use that instead of OUT_DIR in your include!.

1 Like

Thanks for the help. I'll look at those options.

This is my example in the solution's way:

build.rs
include

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.