Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cargo needs a way to run an executable out of a build-dependency #872

Closed
erickt opened this issue Nov 14, 2014 · 14 comments
Closed

Cargo needs a way to run an executable out of a build-dependency #872

erickt opened this issue Nov 14, 2014 · 14 comments

Comments

@erickt
Copy link
Contributor

erickt commented Nov 14, 2014

As best as I can tell, cargo only builds the libraries from a dependency. This has the unfortunate side effect of preventing build scripts from being able to execute a binary. This is unfortunately preventing @dwrensha's capnproto-rust from being used by a cargo build script to generate files.

The reason why is due to the design of the Cap'n Proto project. According to doc, the way Cap'n Proto does code generation for languages is that there is a driver executable, capnp that parses the .capnp file and generates a schema, which is then passed off to a plugin executable to generate the language specific files. There are 3 options to do this right now:

  • build capnproto-rust outside of the project, generate the code and check it into the project.
  • have build.rs clone capnproto-rust, run cargo build in there and execute the binary to generate the code.
  • modify the upstream project to turn capnp into a library, and use that in a build.rs script. I'm not familiar with that codebase, but it does seem pretty big, so this would probably be a lot of work.

Instead, I think things would be a lot more simple if a cargo dependency could be built to include the executables. Then it would be pretty trivial to do this code generation.

@alexcrichton
Copy link
Member

cc #748, a similar issue. I would personally prefer to use libraries for this whenever possible as binaries have the problem of not being able to disambiguate one another. If I have two build dependencies that both provide an executable named foo, then there's no way to differentiate between them.

@dwrensha
Copy link

For what it's worth, my current approach for projects that need to do Cap'n Proto code generation is to require that both executables, capnp and capnpc-rust, already be installed on the system. Then I have a build.rs that invokes capnp compile -orust and puts the generated code in $OUT_DIR. And, of course, I add a Cargo dependency on capnproto-rust, which brings in the runtime library required by the generated code. capnp-rpc-rust is an example that follows this pattern.

@dwrensha
Copy link

Here's one way I could eliminate the need to install capnpc-rust. I could split the code generation library into its own repo / Cargo package. The new package would still have the binary target capnpc-rust, but it would also have a library target that exposes a function that could easily be called from a build.rs file. That function would execute capnp compile -o/bin/cat and forward the output to the capnpc_rust::codegen module.

So you would still need to separately install capnp, but once you do that you'd be able to stay entirely within Cargo.

@erickt
Copy link
Contributor Author

erickt commented Nov 14, 2014

@dwrensha: that would be nifty!

I think this would be useful for other things than capnproto of course. We are eventually going to grow projects as big as Ruby on Rails or Django, which will come along with their own administration tools. I like that cargo provides for project level isolation, So it would be a shame if we force people to have to install something in order to use it.

@wycats: I think you've dealt with this in bundler, how do you handle it?

@alexcrichton
Copy link
Member

I'm going to close this as working as intended, and I'm also not sure that this has come up recently as a desire for various build scripts.

@comex
Copy link

comex commented Feb 26, 2015

I'm trying to do the same thing. I don't see why there is no story for depending on or installing executables other than manually checking out and building... unless I'm missing something?

@alexcrichton
Copy link
Member

Does your build dependency not offer its functionality through the interface of a library? (that is the recommended method)

@comex
Copy link

comex commented Feb 26, 2015

The dependency is bindgen, so yes, but this seems like a waste of time to me... I already have a (hacky) Python script to do some extra processing on the output, so I can't just use the plugin version. I could redo the processing in Rust in order to use the bindgen library (rather than popen), but what is the point of requiring me to do that when the executable is right there in the repo and just needs a way to be compiled?

@alexcrichton
Copy link
Member

what is the point of requiring me to do that when the executable is right there in the repo and just needs a way to be compiled?

Calling a library has a few benefits:

  1. It is far faster than calling a whole separate proces
  2. Avoids conversions between languages through stdout/stderr/args/env vars. You can use native Rust types everywhere (and get compilation warnings, etc)
  3. Cargo can manage everything from the upstream dependencies to what architecture it's built for.
  4. Cargo can precisely tell you where bindgen is. Otherwise a bindgen executable elsewhere in PATH may cause bugs.
  5. Cargo can allow a project to end up running two versions of bindgen if necessary (by saying which links to which)

@torarnv
Copy link

torarnv commented Jul 8, 2015

@alexcrichton: Not all build dependencies can be made into libraries I think. Eg, https://github.com/stepancheg/rust-protobuf, where the binary is a plugin for protoc. Please consider re-opening.

At the moment Cargo effectively builds all deps with --lib, is that what we want? What if I as a package author know that my package consists of both a lib and a binary, wouldn't it make sense for me to be able to override it in my manifest? Or as a user of package, where I know the package provides more than just a lib, wouldn't it make sense be able to declare in the dependency section of my manifest that the dependency should include more than just the lib target?

@per-gron
Copy link

per-gron commented Nov 21, 2017

+1 for reopening this ticket. I am also trying to use protobuf (gRPC in my case), and I really don't like that the way people seem to do it is to ask their users to install grpc_rust_plugin and a bunch of other things globally. I would like my crate to be able to have versioned dependencies to these things.

Performance of the build is a pretty poor argument if the consequence is unversioned dependencies.

@simonbuchan
Copy link

My use case is embedding the dependency binary in the dependant binary. Am I missing some obvious alternative?

@tchernobog
Copy link

tchernobog commented Aug 17, 2021

See also #2267 (comment) for an alternative approach (using cargo run as a wrapper).

tchernobog added a commit to tchernobog/cargo that referenced this issue Aug 30, 2021
TDD testcases for allowing `cargo run` to be invoked
on dependencies of the workspace members via their
corresponding pkgid.

Relates to rust-lang#2267, rust-lang#872.
@zopsicle
Copy link

zopsicle commented Sep 3, 2024

If support is added for build.rs to invoke an executable from a dependency, it would be great if it is also possible to have that dependency be cross-compiled.

My use case for this would be to have build.rs invoke Wine to invoke a cross-compiled Rust executable that invokes a function from a proprietary Windows DLL. Currently I have to jump through hoops to get this to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants