Repository navigation
Description
Current solutions
--message-format=json(Emit more info on --message-format=json #3319, Always produce artifact messages #3752)cargo run --config 'target."cfg(true)".runner = "gdb"to wrap with a commandcargo run --config 'target."cfg(true)".runner = "ls"to find the pathcargo build --artifact-dir <path>(--artifact-dir(nee--out-dir) Tracking Issue #6790)
Hi! This is a followup of #1924 and #1726.
Problem
Sometimes one needs to run an executable produced by Cargo (be it example, binary or doc/unit/integration test) directly.
The most common use case is debugger integration in the IDE, but other examples include strace, perfrecord and similar tools.
Peculiarities
cargo test can produce more than one binary. We need to handle this somehow.
Both cargo run and cargo test have different flavors for specifying profiles, features, targets and arguments for the binary. Ideally, it should be easy to learn the name of output file by the cargo command.
Cargo can setup LD_LIBRARY_PATH when running a binary, it would be good to be able replicate this as well.
Solutions
--with wrapper
Add a --with option to the run family of commands to allow to specify a custom wrapper. There's a stale pull request implementation in #1763.
This would probably be the most useful option on the command line. However, it is not flexible enough: there's still an intermediate Cargo process around, and this won't be enough for IntellJ Rust: we launch debugger process first and then send it a command to launch the debugee.
Also, this can be implemented as an external subcommand on top of other options, so that you can run
cargo with-wrapper strace run --release --bin foo.
--output-file option
We can implement #1706, than clients could specify the name they want. This is a nice option, but it's not perfect: the binary may assume certain location and use something like ../../foo/bar to get resources at run time or something like that. While arguably you should not do this, ideally we should be able able to run binaries exactly as Cargo does. Also with explicit output file the client has to manage temporary directories, which is some extra work.
Stable file names
We can make the names of binaries predictable. This is perhaps the simplest option, but I don't like it for various reasons:
-
We already have
debugandreleaseprofiles, which affect the output path. This means that clients should implement some logic to get the current profile, which may be brittle. -
This requires stabilizing target directory layout and may make future features harder. What if one day we would like to add custom profiles and an ability to configure the custom profile via environment variables/config files?
-
This does not allow to easily derive the binary name given the cargo command, which again means some logic on the client's side.
Add file names to cargo metadata
This is very similar to the previous option, and does not solve the profiles problem at all.
Print the resulting file name with --no-run
This is my favorite option :)
-
It is naturally forward compatible.
-
It's naturally extendable to print more information about the environment which is used to run the process (
LD_LIBRARY_PATH, command line flags). -
We
almostdo this already: with--message-format=jsonwe print the path to binaryif it is not fresh. I suggest that we always output this info, even we don't actually rebuild the binary. Perhaps we should use a new, separate message, to make it easier to extend it to support additional and to make it easier for the clients to separate dependencies from the actual end binaries.(Emit more info on --message-format=json #3319, Always produce artifact messages #3752) -
It would be trivial to match cargo commands with binary names: just add
--no-runoption (I'd like to add this flag tocargo runas well). -
I like that on the client side, you have to actually build the binary before trying to use it. This solves debugging stale binaries problem by construction.
I'll be glad to implement any of these options :)
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
alexcrichton commentedon Feb 10, 2017
Thanks for summarizing the lay of the land here @matklad! I agree with your analysis and I'd be totally down with extending json messages and such. To take that route it sounds like:
cargo run --no-runas that's just the equivalent ofcargo build, right?LD_LIBRARY_PATHwith new entries that need to be added?vojtechkral commentedon Feb 11, 2017
Thanks for the summary!
Your proposal sounds good to me... In my usecase (ie.
gdb,straceet al.) I could do something likegdb $(cargo build --no-run), which sounds allright. It wouldn't be hard to write a short script or Makefile rule for anything more involved...koute commentedon Feb 12, 2017
Yes please!
I recently wrote a Cargo subcommand that needs to know the name of the binaries which are produced after a build, and this was absolutely the biggest pain point I had. Right now I have this really ugly hack where I basically just list all of the files in the target directory that could match whatever the kind of a build I want to run could generate, then I try to build it, and then I see what changed.
I would love to have something like
cargo rustc [other args] --print-output-pathwhich would just tell me what the previous invocation with the exactly the same arguments has generated.In my use case I really don't want to use
--message-format=json, simply because I don't want to do the formatting of the messages myself. What I have is a (not so simple) wrapper aroundcargo rustc, and I just want to pipe anything thatcargo rustcproduces (error messages, etc.) directly into the user's terminal, but still be able to know what output the invocation has generated.malbarbo commentedon Feb 17, 2017
How the proposed approaches works for doc tests (they are ephemeral)? The
--withapproach would work.matklad commentedon Feb 21, 2017
I think neither approach would work out of the box, because it's rustdoc who manages the execution of doctests. That is, any solution we implement in cargo, we need to also implement in rustdoc and then make Cargo to forward command line arguments to rustdoc.
But I think that executing doctests directly is an extremely niche use case, and perhaps we can just not do this?
matklad commentedon Feb 21, 2017
I think I want
run --no-runfor IntelliJ Rust, but this is certainly something we can add or not add later. Let me describe my use case, perhaps it is useful elsewhere.In IDEA UI the rule in general is "everything you can run, you can debug". So the user may launch command line like
cargo run --release --bin foo -- my_program_arg, and then useShift + F10to run, andShift + F9to debug the program, without configuring debugging separately.So I want to be able to get the command line for debugging from the command for running.
One way to do this is to add
--no-runeverywhere, so that Cargo would tell me: "Hey, I've compiled everything, and I would launch this binary with these arguments and in this environment, but I'll let the IDE take over from this point".Another way would be to parse the command line, find out the equivalent
buildcommand and figure the necessary args for the final binary. This parsing of Cargo's command line I would love to avoid, but this is not a major problem.alexcrichton commentedon Feb 21, 2017
Oh so the user is typing in
cargo runand IntelliJ wants to hijack that? I think it's definitely a good idea to avoid parsing arguments and emulating what Cargo is doing, so for that--no-runseems fine to me.vojtechkral commentedon Feb 22, 2017
Side note: Just seeing the literal
cargo run --no-runcommand in the discussion, it really stands out as an obvious oxymoron, something really counter-intuitive and possibly confusing. Perhaps we could move it out of theruncommand entirely and introduce something likecargo path,cargo runcmd,cargo runenvor somesuch?matklad commentedon Feb 22, 2017
A similar issue is that we have
cargo test --no-runin the first place, which does something similar tocargo buildactually.alexcrichton commentedon Feb 22, 2017
Yeah I wouldn't expect a user to type
cargo run --no-run, but having tools be able to insert such a command everywhere seems useful!vojtechkral commentedon Feb 23, 2017
Ok, let me try to summarize:
cargo run --no-runseems to be equivalent tocargo buildcargo build --no-runis confusing, build doesn't run stuff anyway. If the options is meant for printing information out, why not call itcargo build --print? It could also optionally take an argument specifying what things you'd like printed, something likeps -o.cargo test --no-runis, if I'm getting it right, meant to build target in test configuration but not actually run tests. I'm not sure whether this option belongs incargo testor if it rather should be something likecargo build --test, but in any case, it seems to me to be orthogonal to this issue.Correct me if I'm wrong in any of those points...
malbarbo commentedon Feb 23, 2017
@matklad
My use case for executing doctests directly is to collect code coverage. Although some says that doctest should not be used in code coverage, some functions have doctests that are good enough and not running them would decrease the coverage.
alexcrichton commentedon Feb 23, 2017
@vojtechkral did you see @matklad's description above? It sounds like users are typing
cargo runand then IntelliJ would have to intepret the command line and translate that tocargo build, whereas slapping on a--no-runwould be much eaiser.And currently
cargo build --testisn't equivalent tocargo test --no-runbecause the former builds one test while the latter builds all tests.vojtechkral commentedon Feb 24, 2017
@alexcrichton I must have missed that post or misunderstood it earlier. Sorry.
I'm not convinced that's a good reasoning, though. I don't think it's a good idea to make cargo's interface tailored to IntelliJ. Picture a few months/years down the road a newbie comes along:
cargo run --no-runorcargo build --no-runCould we instead perhaps separate "no running" and "printing"? Or maybe even better make "printing" imply "not running", where applicable? Ie.
--printwould make sense withcargo buildand it would suppress running withcargo runandcargo test, which would make more sense to me that the other way around...cargo build --testsperhaps?I guess my problem with the
--no-runflag is that it basically promotes an interface along the lines ofcargo dosomething --wait-dont-actually-do-that-do-something-else.I hope I'm making sense. Anyway, obvisouly, that's just my $.02, if you guys come to the conclusion
--no-runis ok enough, that's fine by me, it's IMO still an improvement over--with.matklad commentedon Feb 27, 2017
@vojtechkral totally agree with you that
--no-runinterface feels ugly if used directly by the user.I think the core problem is that there are actually two issues we are trying to solve
Issue 1
"I need to find out where the artifacts are"
This issues should be covered by #3319 and #3752. This info is currently available in JSON only, but I think we could expose it in the human readable format if
--verboseflag is present. So, the answer to " How do I get a Rust binary built and a path to it printed? I need that for XYZ." would be: "if you need it for one-off thing in human format, run cargo with --verbose flag. If you are integrating cargo with other tools, use--message-format=json".Issue 2
"What is the command line invocation (that is, binary path, command line arguments, environment variables) that cargo would use for my binary? I would love to intercept it exactly"
This is a rather more specific use case. We need it for IntelliJ Rust, but I imagine other usecases. For example, one can implement a command
cargo-dbgwith the following interface:User runs
cargo test --test foo bar::baz -- --no-captureand sees a test failure. To debug it, they just typegdbin front:cargo gdb test --test foo bar::baz -- --no-capture.cargo-gdbcommand then ideally should be to just run the originalcargo test --test foo bar::baz -- --no-captureand intercept the test binary invocation.One way to do it is to add a special argument,
--no-run, or--intercept. An interesting alternative would be to set a special environment variable: I think it might be even a better solution, because it won't clutter the command line interface intended for humans, and at the same time would allow tools to avoid even slight modifications of original command line.14 remaining items
anordal commentedon Sep 20, 2020
For integration test purposes, I couldn't be happier about the new
CARGO_BIN_EXE_<name>variable (#7697), released in Cargo 1.43.0.ensc commentedon Sep 7, 2022
What is the current recommendation to get the final output name? In my use case, the build consists of two phases:
cargo build)objcopy -O binary ...)Both steps are more or less independent; IDEs or external scripts might have their own idea about the
--message-format=jsonoutput in the first step so that these information are not available for step 2.Hence, step 2 needs to determine the path to the binary somehow and atm it works only by guessing and relying on the usual behavior (-->
cargo metadata | sed 's!.*"target_directory":"\([^"]\+\)",.*!\1!'+ external information about "debug" or "release" + known binary name).weihanglo commentedon Sep 7, 2022
Another workaround with
jq. Use at your own risk:ensc commentedon Sep 8, 2022
I think, using
cargo buildto get this information is the wrong way and not applicable. It does the actual build and:There should be at least some passive command like
cargo metadatawhich outputs these information. But atm, itdebugvsreleasedirectories--targetmatrach commentedon Sep 3, 2023
It seems that
cargo run --no-runcan be somewhat emulated with a custom target runner. For example:cargo --config "target.'cfg(unix)'.runner = 'ls'" runIn contrast to
cargo +nightly build --out-dir=out_dir -Z unstable-options, it is a bit racy, as the.cargo-lockonly covers the build step. So usingcp -t out_dir/as the runner, while attempting multiple builds in the same target, may copy the wrong binary.davehorner commentedon Jun 11, 2025
I would like to see an option like --no-run / --no-build / --cached
#11507
as it says in the initial post edit: EDIT: 90% of this is solved by --message-format=json flag.
having the data available to capture and get the path is not much of a solution to someone looking to run binaries directly.
as mentioned in #11507 is what we're tacking together to make something work because
cargodoesn't make this easy.I wrote https://crates.io/crates/cargo-e and it has a --cached option that just attempts to run the typical target release/debug paths if they exist.
https://github.com/davehorner/cargo-e/blob/bf9885d1eb402473033e3668ce9f4c7a9ec8c81b/cargo-e/src/e_command_builder.rs#L1909
I have a filtering mechanism and will eventually create a database to store the resolved paths for targets, the above simple existence check has been enough for most of the code that I'm running against.
I have not specifically spent any time on --cached when used with tests yet either.
anyway, to actually run the existing target you just do
if the usual target file exists release checked then debug paths it runs it, otherwise it does the typical
cargo run#15275 -
cargo bench --no-runclosed "Cargo already provides executable paths for both human and machine" this doesn't work for run and you have to go into json messages to get the data you're after in other scenarios.epage commentedon Jun 11, 2025
We have a project goal for plumbing commands. The hope is this can provide better context for what is happening than
cargo metadata. There is a GSoC project that is working towards this. Unsure if it will fully be able to resolve the need here as we're still working to define all of this.miikkas commentedon Sep 14, 2025
Backlinking a recent duplicate thread on IRLO, since it may have some additional useful context for this feature and there are some tips on how to currently achieve the functionality.
In sum, the following in a project's
.cargo/config.tomlmay help when the binary name ofcargo runis needed:ensc commentedon Sep 15, 2025
Will this work without doing the actual build?
One of the main reasons to know the path is the integration in a build system which does some post-processing. E.g.
miikkas commentedon Sep 15, 2025
Unfortunately not. And I'd like to see such feature as well. Meanwhile, for some manual local terminal work such as invoking a separate tool (e.g.
rust-gdb,hyperfine,ldd, etc.) the alias workaround can be useful.sunshowers commentedon Nov 17, 2025
FWIW nextest now supports running tests under a debugger: https://nexte.st/docs/integrations/debuggers/