-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Need a reliable way to get the target dir from the build script #9661
Comments
And grabbing the |
I've been recently working on a project where there's need to link the generated binary in user project to another directory and I couldn't find a reliable way to get the target directory either. |
This has cause and effect backwards. |
Yes, I since noticed that this is an optional input to Cargo: https://doc.rust-lang.org/cargo/reference/environment-variables.html Indeed, that's not what I want. I really need the target dir for the most dependent crate being directly built. I've tried numerous unsatisfying hacks. The one that works the most reliably is to read the first path from the
Although this only works on Windows. And I don't know how "supported" this is but I haven't found a better solution. |
Can you provide more information on exactly why you need the target directory? Cargo is designed so that build scripts are intentionally constrained on what they should do, and their only interaction should be through the OUT_DIR. |
@ehuss Kenny is out this week but some scenarios are listed here microsoft/windows-rs#979 (comment). I'll take a stab here at paraphrasing: The For example, envision a developer's crate depending on
One proposed change is to provide a pointer of sorts to the developer's crate target directory and all dependent crates could write their metadata to that path for consumption. Open to other ideas, of course. |
@riverar you're making the same mistake as #9661 (comment), |
@jyn514 Ah right, forgot. Thanks! This doesn't affect the proposed change much. The bottom line is we'd like a pointer to the target directory for the aforementioned reasons. |
One use-case I have is to store build-script's own caches. I can use a temporary directory as a scratch space (though it might still be nice to place this somewhere inside For example, if I am building some C code, I want to put intermediate |
Eg, storing `.o` files in OUT_DIR is ok! See rust-lang#9661 (comment) for some discussion.
near-test-contracts builds some wasm contracts for use in testing. It does so by recursively invoking `cargo` from `build.rs`. Before this commit, we tried to re-use parent's `CARGO_TARGET_DIR` to figure out where we should put the data. That was rather hacky, as cargo doesn't expose that information to the build scripts in a reliable way: rust-lang/cargo#9661 (comment) Naturally, our hacks broken when when the `CARGO_TARGET_DIR` was set to a relative path, because build.rs doesn't know where workspace root lives. The fix is to use `OUT_DIR` rather than `CARGO_TARGET_DIR`, which I think is the supported way to this in the first place. Eg, the `cc` crate uses `OUT_DIR` to store intermediate `.o` files, which I think matches our use case pretty closely.
near-test-contracts builds some wasm contracts for use in testing. It does so by recursively invoking `cargo` from `build.rs`. Before this commit, we tried to re-use parent's `CARGO_TARGET_DIR` to figure out where we should put the data. That was rather hacky, as cargo doesn't expose that information to the build scripts in a reliable way: rust-lang/cargo#9661 (comment) Naturally, our hacks broken when when the `CARGO_TARGET_DIR` was set to a relative path, because build.rs doesn't know where workspace root lives. The fix is to use `OUT_DIR` rather than `CARGO_TARGET_DIR`, which I think is the supported way to this in the first place. Eg, the `cc` crate uses `OUT_DIR` to store intermediate `.o` files, which I think matches our use case pretty closely.
doc: it's valid to use OUT_DIR for intermediate artifacts Eg, storing `.o` files in OUT_DIR is ok! See #9661 (comment) for some discussion.
near-test-contracts builds some wasm contracts for use in testing. It does so by recursively invoking `cargo` from `build.rs`. Before this commit, we tried to re-use parent's `CARGO_TARGET_DIR` to figure out where we should put the data. That was rather hacky, as cargo doesn't expose that information to the build scripts in a reliable way: rust-lang/cargo#9661 (comment) Naturally, our hacks broken when when the `CARGO_TARGET_DIR` was set to a relative path, because build.rs doesn't know where workspace root lives. The fix is to use `OUT_DIR` rather than `CARGO_TARGET_DIR`, which I think is the supported way to this in the first place. Eg, the `cc` crate uses `OUT_DIR` to store intermediate `.o` files, which I think matches our use case pretty closely.
+1 for this issue as we are distributing dynamic libraries that are hard to build (especially on Windows) internally (by automatically downloading them from a server), and these libs needs to be in the same directory as the final executable. Without a reliable way to determine |
FWIW, It expects the path stored in the OUT_DIR environment variable as input. I'm not certain it handles all possible situations though. |
I'm working on cxx-qt, a code generator build on top of cxx. I need a stable path to output generated C++ headers from build.rs for C++ build systems to be able to find them. I've seen what cxx_build does by walking up from OUT_DIR but that feels quite hacky to me. The least worst idea I've come up with is explicitly setting the CARGO_TARGET_DIR environment variable from the C++ build system so it gets passed to build.rs. I wish I did not have to do this though. Of course, this workaround isn't available if you're not working with an automated system that invokes cargo. |
On further thought, for cxx-qt (and cxx), perhaps setting the CARGO_TARGET_DIR environment variable isn't the best idea. I don't want to depend on unstable implementation details of the structure of the target directory. I think it would be better to use an environment variable specific to this purpose. Or better yet, a designated place to put exported build artifacts (#5457). I think this is quite a different use case from the windows crate. Please correct me if I'm wrong, but I think what @kennykerr and @riverar are asking for is a way to pass data from the build script of a dependency to downstream build scripts. I'm not sure exposing the target directory to build scripts is a great way to do that either. cxx_build uses a public static mut struct together with |
I think it is quite silly that at present it is difficult for a crate to discover where it is currently being built |
@epage - Yes, although it may be more than one file - for example you typically need both server.dll and server.pdb... @Be-ing - I am not talking about interop with other build systems or interop with C++. This is all just Rust code and crates. When its all tested and done, I may end up taking the DLL and shipping it to customers outside of the Rust universe but that's a separate issue that is outside the scope of this problem, at least for me. I will check out artifact dependencies. |
I did some experimenting with artifact dependencies. Very promising! I can use the This is required for safe DLL loading on Windows via |
Should anyone else stumble upon this, here's an example.
[package]
name = "server"
edition = "2021"
[lib]
crate-type = ["cdylib"]
#[no_mangle]
unsafe extern "system" fn server() -> i32 {
println!("hello server");
123
}
[package]
name = "client"
edition = "2021"
[build-dependencies.server]
path = "../server"
artifact = "cdylib"
[dependencies.windows-sys]
version = "0.52"
features = ["Win32_Foundation", "Win32_System_LibraryLoader"]
fn main() {
let var = std::env::var("CARGO_CDYLIB_DIR_SERVER").expect("var not found");
let from = std::path::Path::new(&var);
let to = from
.parent()
.expect("parent not found")
.parent()
.expect("parent not found")
.parent()
.expect("parent not found");
for entry in std::fs::read_dir(from).expect("from not found") {
let from = entry.expect("entry not found").path();
std::fs::copy(
&from,
to.with_file_name(from.file_name().expect("file name not found")),
)
.expect("copy failed");
}
}
fn main() {
unsafe {
use windows_sys::{core::*, Win32::System::LibraryLoader::*};
let library = LoadLibraryExW(w!("server.dll"), 0, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
let address = GetProcAddress(library, s!("server")).expect("server not found");
let server: extern "system" fn() -> i32 = std::mem::transmute(address);
assert_eq!(server(), 123);
}
} Run as follows: It's the client build script that continues to be problematic. Is there a way to reliably determine where Cargo will place the client binary/test executable? |
What we do in debug builds is set the env variable and use it to locate the DLL, in release builds we rely on executable dir. |
@kennykerr maybe the dynamic library paths that can be set from your build.rs build script via |
What path would I add? |
@epage I would, I'm not interested in generating C++ headers, but I do want to generate man pages and shell completion files from build.rs of my crate, in a predictable location for external packaging tools (e.g. debian, arch linux, etc). There doesn't seem to be any good solution currently. |
@VorpalBlade it sounds like you are disagreeing with me but I'm not sure on what. My comment was about us not talking abstractly about this problem but speaking in terms of concrete use cases, like your man page use case. |
@epage Ah then I believe I misunderstood you as wanting to only concentrate on the specific case of generating headers. This is what I currently do:
This is quite hacky on the PKGBUILD side:
This breaks down if there are multiple out dirs as happens when you are developing and changing the package. During development it also leaves cruft in the build tree that is useless: ❯ find target/debug/ -name paketkoll.1 | wc -l
26 That really shouldn't be there in so many copies. In fact I see no reason for cargo retaining all of that. Some sort of garbage collection would be in order for any packages that are part of your workspace (and thus are unlikely to be around in multiple versions at the same time and are also like to change a lot). |
GC is being tracked at #12633. Our initial focus is on global resources and then target directories as a whole. We'll then evaluate what doing it inside of a target-dir looks like. However, I think its unlikely that we'd leave only one instance present. |
Fair enough, what about the rest of the use case though? As you can see, the current solution is hacky and suboptimal. |
Yes, if I'm understanding correctly, this is like the C++ use cases where they are wanting to have |
I propose to the Cargo team that we close this issue with the encouragement that people move their discussion to more specific, use-case focused issues, creating them where needed. As mentioned in #9661 (comment), there are fundamental problems with Tracked use cases:
Use cases where I could not find an Issue tracking it:
|
Having watched this issue and a number of the related ones for a while, that sounds to me like a plan that will effectively make sure this is not addressed. When the individual use cases discuss this topic the answer is usually along the lines of "this may affect other things too so we can't change it". Having at least one meta issue open tracking the multiple use cases and how they interact seems like the only way likely to actually reach a conclusion. |
In looking at the use cases I just summarized, they are so unrelated to each other that I don't see how having a tracking issue for them would help with coordination across them. As for your concern, I can't speak too much to that without examples as that failure case does not ring a bell. |
The main issue gives us a way to consolidate power and make it clear a number of different use cases could all benefit from the same improved functionality in |
- rust-lang/cargo#9661 (comment) CARGO_TARGET_DIR from cargo.toml is not available during build scripts at the moment
I think I've just stumbled here with another use case.. ? My cargo project is going to be distributed as an executable, as my colleagues don't understand or need rust or libs. I think code in this later binary needs a reliable way to retrieve a valid path to the |
For a use case like that, I assume what |
The problem: I need to locate the target dir from the build script.
The solution: I'd love a
CARGO_TARGET_DIR
environment variable. There's clearly precedent for this ascargo doc
depends on such a thing today.@jyn514 suggested I reach out to the Cargo team for advice.
Currently the Windows crate has been using various hacky solutions that are problematic.
rust-lang/docs.rs#1444 (comment)
The text was updated successfully, but these errors were encountered: