Distinguishing `cargo run` from `cargo build` for exporting files

I'm again at a framework where files may be contributed to the app's installation directory.

The debug_assertions constant may be used to detect cargo build --release, but I wanted some consensus on what is best to do here.

In my framework the user could do this at the Cargo manifest:

[package.metadata.fl]
files = [
    { from = "assets/**/*.webp", to = "img" }
]

This would make it so the source path ~/assets/flowers/red.webp may be read during runtime using the app://img/flowers/red.webp URL (and the actual file will be exported at that path as well).

When debugging (e.g. cargo running an app), I want to avoid exporting these files, therefore bootstrapping the application with the (from, to) entries so I do some magic when resolving app: URLs for the user. (In the HTML5 target, app: is resolved via HTTP requests, while in native targets I'd use std::fs and the executable path).

What leaves me confused is whether I detect cargo run solely through debug_assertions or if there's something more reliable to use (because, then, even a simple cargo build can't be used correctly as I'd not export files when debugging with the debug_assertions approach).

I suppose using an explicit feature is the only way to detect cargo run.

[features]
run_flag = []

Then:

#[cfg(feature = "run_flag")]

I forgot about that, but I'd really want something more built-in.

Regarding the title question in general: The user expectation should always be that cargo run will do precisely the same thing as cargo build, followed by executing the compiled binary. In fact, running cargo run after cargo build is usually expected not to re-do any building steps, and running cargo build after cargo run is usually a no-op; that is, if no source files changed in the meantime.

The choice of profiles is orthogonal, in particular, either command has a default (debug) variant, and a --release variant.

As you noted, crate feature flags can be one way to provide customization of the build process, however crate features do interact with the dependency solver in ways that could influence their usefulness for fully “custom” ways[1] of influencing the built process, in case the crate in question is just a "library crate". In this case, possibly environment variables may be more fitting; those can be read & tracked in build scripts as described here.

Your question sounds more like this is a "framework" in the sense of involving a particular format of overall project structure, so we can consider more flexible approaches when controlling the crate that starts the compilation.

If I understand the question correctly, and the desired behavior of what you call cargo run is something that will only be sensible if run in (otherwise surprisingly) specific ways directly from the source directory… one possibility could be to let the user not use cargo run to begin with. The local cargo config files can define (conveniently) custom subcommands (via aliases)… while you cannot change the meaning of cargo run itself, you could follow e.g. more or less precisely the "cargo xtask" design pattern, and have it called anything from cargo xtask run to cargo debugrun or whichever naming scheme you like. That alias can then call cargo run for you while setting a fitting environment variable (or compiler flag?), that controls the build process as desired, while ensuring everything runs from the right place.


  1. which don’t fit the typical model of: enabling some functionality and/or API, in a way that’s additive and controlled collectively by all the downstream dependent crates in a dependency graph ↩︎

1 Like

That idea of using environment variables isn't bad (seems more convenient than a feature), butenv! requires a known environment variable at compile-time.

My issue with env! isn't an issue at all, because, as you've shown, I can have custom commands for my framework (I'm thinking of a CLI though) so they are always passed, but....

What about when running cargo publish? How do I prevent that env! lookup error during crate verification?

AAH, right, option_env!