Skip to content

Commit b825dce

Browse files
committed
Find winmd files without reading PATH nor copying to target/
Non-Windows platforms - which are supported for cross-compiling - do not set the output directory in `PATH` nor use semicolons to separate this variable, resulting in errors Split out `winmd` "discovery" by emitting the path to these files in a build step, that is subsequently compiled into `windows_gen`. This is more efficient than `include_bytes!` which was used prior to the `PATH` system as no copies or binary includes are required at all. Copying of DLL targets to the `target/` dir for easy consumption and running of crates is still performed, but only on Windows for the same `PATH` reason. In the future, if a chain of crates is required to export `winmd` files for downstream crates to consume, this can be extended to also export their `$CARGO_MANIFEST_DIR/.winmd/windows` in such a way that the next crate can pick it up, again without copying any files around.
1 parent 1dd9a40 commit b825dce

File tree

3 files changed

+70
-76
lines changed

3 files changed

+70
-76
lines changed

crates/gen/build.rs

+19-31
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,24 @@
1-
#[cfg(windows)]
21
fn main() {
3-
let mut source: ::std::path::PathBuf = ::std::env::var("CARGO_MANIFEST_DIR")
4-
.expect("No `CARGO_MANIFEST_DIR` env var")
5-
.into();
2+
let path_file =
3+
::std::path::Path::new(&::std::env::var("OUT_DIR").expect("No `OUT_DIR` env var"))
4+
.join("winmd_path");
65

7-
source.push(".windows");
8-
source.push("winmd");
6+
println!("cargo:rerun-if-changed={}", path_file.display());
7+
println!("cargo:rerun-if-env-changed=CARGO_MANIFEST_DIR");
98

10-
let destination = std::env::var("PATH").expect("No `PATH` env variable set");
11-
let end = destination.find(';').expect("Path not ending in `;`");
12-
let mut destination: std::path::PathBuf = destination[..end].into();
13-
destination.pop();
14-
destination.pop();
15-
destination.push(".windows");
16-
destination.push("winmd");
9+
// Compute and store the absolute path to this crates' CARGO_MANIFEST_DIR, in which
10+
// winmd files reside. This allows dependent crates - which otherwise have no idea
11+
// about the existence of our source directory - to find and use these files.
12+
let source = ::std::path::Path::new(
13+
&::std::env::var("CARGO_MANIFEST_DIR").expect("No `CARGO_MANIFEST_DIR` env var"),
14+
)
15+
.join(".windows/winmd");
1716

18-
if let ::std::result::Result::Ok(files) = ::std::fs::read_dir(source) {
19-
for file in files.filter_map(|file| file.ok()) {
20-
if let ::std::result::Result::Ok(file_type) = file.file_type() {
21-
if file_type.is_file() {
22-
let path = file.path();
23-
if let ::std::option::Option::Some(filename) = path.file_name() {
24-
let _ = std::fs::create_dir_all(&destination);
25-
destination.push(filename);
26-
let _ = ::std::fs::copy(path, &destination);
27-
destination.pop();
28-
}
29-
}
30-
}
31-
}
32-
}
17+
::std::fs::write(
18+
path_file,
19+
source
20+
.to_str()
21+
.expect("CARGO_MANIFEST_DIR is not a valid UTF-8 string"),
22+
)
23+
.expect("Failed to write winmd path to src/winmd_path.rs");
3324
}
34-
35-
#[cfg(not(windows))]
36-
fn main() {}

crates/gen/src/workspace.rs

+6-13
Original file line numberDiff line numberDiff line change
@@ -93,26 +93,19 @@ fn get_crate_winmds() -> Vec<File> {
9393

9494
let mut result = vec![];
9595

96+
// Manifest directory of the crate calling `build!`
9697
if let Ok(dir) = std::env::var("CARGO_MANIFEST_DIR") {
9798
let mut dir: std::path::PathBuf = dir.into();
9899
dir.push(".windows");
99100
dir.push("winmd");
100101
push_dir(&mut result, &dir);
101102
}
102103

103-
let dir = std::env::var("PATH").expect("No `PATH` env variable set");
104-
let end = dir.find(';').expect("Path not ending in `;`");
105-
let mut dir: std::path::PathBuf = dir[..end].into();
106-
dir.pop();
107-
dir.pop();
108-
dir.push(".windows");
109-
dir.push("winmd");
110-
push_dir(&mut result, &dir);
111-
112-
let mut dir: std::path::PathBuf = target_dir().into();
113-
dir.push(".windows");
114-
dir.push("winmd");
115-
push_dir(&mut result, &dir);
104+
// Manifest directory of windows-rs' `windows_gen` crate
105+
push_dir(
106+
&mut result,
107+
std::path::Path::new(include_str!(concat!(env!("OUT_DIR"), "/winmd_path"))),
108+
);
116109

117110
result
118111
}

crates/macros/src/lib.rs

+45-32
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,8 @@ impl ToTokens for RawString {
4444
pub fn build(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
4545
let build = parse_macro_input!(stream as BuildMacro);
4646
let tokens = RawString(build.to_tokens_string());
47-
let target_dir = std::env::var("PATH").expect("No `PATH` env variable set");
48-
let end = target_dir.find(';').expect("Path not ending in `;`");
49-
let target_dir = RawString(target_dir[..end].to_string());
5047

51-
let tokens = quote! {
48+
let emit_windowsrs_tokens = quote! {
5249
{
5350
// The following must be injected into the token stream because the `OUT_DIR` and `PROFILE`
5451
// environment variables are only set when the build script run and not when it is being compiled.
@@ -64,23 +61,38 @@ pub fn build(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
6461
let mut cmd = ::std::process::Command::new("rustfmt");
6562
cmd.arg(&path);
6663
let _ = cmd.output();
64+
}
65+
};
66+
67+
#[cfg(windows)]
68+
let target_dir = {
69+
// PATH used to copy dlls to the target directory of the outmost crate,
70+
// expected to point to its `<taret_dir>\profile\deps` folder:
71+
// https://github.com/rust-lang/cargo/issues/9661#issuecomment-877468095
72+
// This is not the case on non-Windows platforms, hence no libraries
73+
// will be copied.
74+
let target_dir = std::env::var("PATH").expect("No `PATH` env variable set");
75+
let end = target_dir.find(';').expect("Path not ending in `;`");
76+
RawString(target_dir[..end].to_string())
77+
};
6778

79+
#[cfg(windows)]
80+
let emit_libcopy_tokens = quote! {
81+
{
6882
fn copy(source: &::std::path::Path, destination: &mut ::std::path::PathBuf) {
6983
if let ::std::result::Result::Ok(entries) = ::std::fs::read_dir(source) {
7084
for entry in entries.filter_map(|entry| entry.ok()) {
71-
if let ::std::result::Result::Ok(entry_type) = entry.file_type() {
72-
let path = entry.path();
73-
if let ::std::option::Option::Some(last_path_component) = path.file_name() {
74-
let _ = ::std::fs::create_dir_all(&destination);
75-
destination.push(last_path_component);
76-
if entry_type.is_file() {
77-
let _ = ::std::fs::copy(path, &destination);
78-
} else if entry_type.is_dir() {
79-
let _ = ::std::fs::create_dir(&destination);
80-
copy(&path, destination);
81-
}
82-
destination.pop();
85+
let path = entry.path();
86+
if let ::std::option::Option::Some(last_path_component) = path.file_name() {
87+
let _ = ::std::fs::create_dir_all(&destination);
88+
destination.push(last_path_component);
89+
if path.is_file() {
90+
let _ = ::std::fs::copy(path, &destination);
91+
} else if path.is_dir() {
92+
let _ = ::std::fs::create_dir(&destination);
93+
copy(&path, destination);
8394
}
95+
destination.pop();
8496
}
8597
}
8698
}
@@ -89,15 +101,13 @@ pub fn build(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
89101
fn copy_to_profile(source: &::std::path::Path, destination: &::std::path::Path, profile: &str) {
90102
if let ::std::result::Result::Ok(files) = ::std::fs::read_dir(destination) {
91103
for file in files.filter_map(|file| file.ok()) {
92-
if let ::std::result::Result::Ok(file_type) = file.file_type() {
93-
if file_type.is_dir() {
94-
let mut path = file.path();
95-
if let ::std::option::Option::Some(filename) = path.file_name() {
96-
if filename == profile {
97-
copy(source, &mut path);
98-
} else {
99-
copy_to_profile(source, &path, profile);
100-
}
104+
let mut path = file.path();
105+
if path.is_dir() {
106+
if let ::std::option::Option::Some(filename) = path.file_name() {
107+
if filename == profile {
108+
copy(source, &mut path);
109+
} else {
110+
copy_to_profile(source, &path, profile);
101111
}
102112
}
103113
}
@@ -130,17 +140,20 @@ pub fn build(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
130140

131141
let profile = ::std::env::var("PROFILE").expect("No `PROFILE` env variable set");
132142
copy_to_profile(&source, &destination, &profile);
133-
134-
destination.push(".windows");
135-
destination.push("winmd");
136-
source.pop();
137-
source.push("winmd");
138-
copy(&source, &mut destination);
139143
}
140144
}
141145
};
142146

143-
tokens.as_str().parse().unwrap()
147+
#[cfg(not(windows))]
148+
let emit_libcopy_tokens = quote!();
149+
150+
(quote! {
151+
#emit_windowsrs_tokens
152+
#emit_libcopy_tokens
153+
})
154+
.as_str()
155+
.parse()
156+
.unwrap()
144157
}
145158

146159
#[proc_macro]

0 commit comments

Comments
 (0)