Skip to content

Commit

Permalink
Find winmd files without reading PATH nor copying to target/
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
MarijnS95 committed Aug 13, 2021
1 parent 1dd9a40 commit b825dce
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 76 deletions.
50 changes: 19 additions & 31 deletions crates/gen/build.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,24 @@
#[cfg(windows)]
fn main() {
let mut source: ::std::path::PathBuf = ::std::env::var("CARGO_MANIFEST_DIR")
.expect("No `CARGO_MANIFEST_DIR` env var")
.into();
let path_file =
::std::path::Path::new(&::std::env::var("OUT_DIR").expect("No `OUT_DIR` env var"))
.join("winmd_path");

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

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

if let ::std::result::Result::Ok(files) = ::std::fs::read_dir(source) {
for file in files.filter_map(|file| file.ok()) {
if let ::std::result::Result::Ok(file_type) = file.file_type() {
if file_type.is_file() {
let path = file.path();
if let ::std::option::Option::Some(filename) = path.file_name() {
let _ = std::fs::create_dir_all(&destination);
destination.push(filename);
let _ = ::std::fs::copy(path, &destination);
destination.pop();
}
}
}
}
}
::std::fs::write(
path_file,
source
.to_str()
.expect("CARGO_MANIFEST_DIR is not a valid UTF-8 string"),
)
.expect("Failed to write winmd path to src/winmd_path.rs");
}

#[cfg(not(windows))]
fn main() {}
19 changes: 6 additions & 13 deletions crates/gen/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,26 +93,19 @@ fn get_crate_winmds() -> Vec<File> {

let mut result = vec![];

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

let dir = std::env::var("PATH").expect("No `PATH` env variable set");
let end = dir.find(';').expect("Path not ending in `;`");
let mut dir: std::path::PathBuf = dir[..end].into();
dir.pop();
dir.pop();
dir.push(".windows");
dir.push("winmd");
push_dir(&mut result, &dir);

let mut dir: std::path::PathBuf = target_dir().into();
dir.push(".windows");
dir.push("winmd");
push_dir(&mut result, &dir);
// Manifest directory of windows-rs' `windows_gen` crate
push_dir(
&mut result,
std::path::Path::new(include_str!(concat!(env!("OUT_DIR"), "/winmd_path"))),
);

result
}
77 changes: 45 additions & 32 deletions crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,8 @@ impl ToTokens for RawString {
pub fn build(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
let build = parse_macro_input!(stream as BuildMacro);
let tokens = RawString(build.to_tokens_string());
let target_dir = std::env::var("PATH").expect("No `PATH` env variable set");
let end = target_dir.find(';').expect("Path not ending in `;`");
let target_dir = RawString(target_dir[..end].to_string());

let tokens = quote! {
let emit_windowsrs_tokens = quote! {
{
// The following must be injected into the token stream because the `OUT_DIR` and `PROFILE`
// environment variables are only set when the build script run and not when it is being compiled.
Expand All @@ -64,23 +61,38 @@ pub fn build(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut cmd = ::std::process::Command::new("rustfmt");
cmd.arg(&path);
let _ = cmd.output();
}
};

#[cfg(windows)]
let target_dir = {
// PATH used to copy dlls to the target directory of the outmost crate,
// expected to point to its `<taret_dir>\profile\deps` folder:
// https://github.com/rust-lang/cargo/issues/9661#issuecomment-877468095
// This is not the case on non-Windows platforms, hence no libraries
// will be copied.
let target_dir = std::env::var("PATH").expect("No `PATH` env variable set");
let end = target_dir.find(';').expect("Path not ending in `;`");
RawString(target_dir[..end].to_string())
};

#[cfg(windows)]
let emit_libcopy_tokens = quote! {
{
fn copy(source: &::std::path::Path, destination: &mut ::std::path::PathBuf) {
if let ::std::result::Result::Ok(entries) = ::std::fs::read_dir(source) {
for entry in entries.filter_map(|entry| entry.ok()) {
if let ::std::result::Result::Ok(entry_type) = entry.file_type() {
let path = entry.path();
if let ::std::option::Option::Some(last_path_component) = path.file_name() {
let _ = ::std::fs::create_dir_all(&destination);
destination.push(last_path_component);
if entry_type.is_file() {
let _ = ::std::fs::copy(path, &destination);
} else if entry_type.is_dir() {
let _ = ::std::fs::create_dir(&destination);
copy(&path, destination);
}
destination.pop();
let path = entry.path();
if let ::std::option::Option::Some(last_path_component) = path.file_name() {
let _ = ::std::fs::create_dir_all(&destination);
destination.push(last_path_component);
if path.is_file() {
let _ = ::std::fs::copy(path, &destination);
} else if path.is_dir() {
let _ = ::std::fs::create_dir(&destination);
copy(&path, destination);
}
destination.pop();
}
}
}
Expand All @@ -89,15 +101,13 @@ pub fn build(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
fn copy_to_profile(source: &::std::path::Path, destination: &::std::path::Path, profile: &str) {
if let ::std::result::Result::Ok(files) = ::std::fs::read_dir(destination) {
for file in files.filter_map(|file| file.ok()) {
if let ::std::result::Result::Ok(file_type) = file.file_type() {
if file_type.is_dir() {
let mut path = file.path();
if let ::std::option::Option::Some(filename) = path.file_name() {
if filename == profile {
copy(source, &mut path);
} else {
copy_to_profile(source, &path, profile);
}
let mut path = file.path();
if path.is_dir() {
if let ::std::option::Option::Some(filename) = path.file_name() {
if filename == profile {
copy(source, &mut path);
} else {
copy_to_profile(source, &path, profile);
}
}
}
Expand Down Expand Up @@ -130,17 +140,20 @@ pub fn build(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {

let profile = ::std::env::var("PROFILE").expect("No `PROFILE` env variable set");
copy_to_profile(&source, &destination, &profile);

destination.push(".windows");
destination.push("winmd");
source.pop();
source.push("winmd");
copy(&source, &mut destination);
}
}
};

tokens.as_str().parse().unwrap()
#[cfg(not(windows))]
let emit_libcopy_tokens = quote!();

(quote! {
#emit_windowsrs_tokens
#emit_libcopy_tokens
})
.as_str()
.parse()
.unwrap()
}

#[proc_macro]
Expand Down

0 comments on commit b825dce

Please sign in to comment.