natvis_pdbs/
lib.rs

1use serde::*;
2
3use std::fs;
4use std::ffi::OsString;
5use std::path::*;
6use std::process::*;
7
8
9
10macro_rules! fatal {
11    ( $($tt:tt)* ) => {{
12        eprintln!($($tt)*);
13        std::process::exit(1);
14    }};
15}
16
17#[derive(Deserialize)] struct Metadata { packages: Vec<Package> }
18#[derive(Deserialize)] struct Package { name: String, manifest_path: PathBuf }
19
20pub fn metabuild() {
21    if !std::env::var("TARGET").ok().unwrap_or(String::new()).ends_with("-msvc") {
22        return; // Not an -msvc target, better not use MSVC specific linker flags.
23    }
24
25    let output = Command::new("cargo").arg("metadata").arg("--format-version").arg("1").stderr(Stdio::inherit()).output();
26    let output = output.unwrap_or_else(|err| fatal!("natvis-pdbs failed to execute `cargo metadata --format-version 1`: {}", err));
27    match output.status.code() {
28        Some(0) => {},
29        Some(n) => fatal!("`cargo metadata --format-version 1`: exit code {}", n),
30        None    => fatal!("`cargo metadata --format-version 1`: terminated by signal"),
31    }
32
33    let metadata : Metadata = serde_json::from_slice(&output.stdout[..]).unwrap_or_else(|err| fatal!("natvis-pdbs failed to parse `cargo metadata --format-version 1`: {}", err));
34
35    println!("cargo:rerun-if-env-changed=LINK");
36    let mut link_args = std::env::var_os("LINK").unwrap_or(OsString::new());
37    let mut link_args_changed = false;
38
39    for package in metadata.packages.iter() {
40        let root = if let Some(p) = package.manifest_path.parent() {
41            p
42        } else {
43            println!("cargo:warning=Package {:?}'s manifest_path {:?} has no parent directory, skipping in search for natvis files...", &package.name, &package.manifest_path);
44            continue
45        };
46
47        let src = root.join("src");
48
49        let dm  = root.join("debug_metadata");
50        //  https://github.com/Lokathor/tinyvec/pull/167#issuecomment-1238471549
51        //  https://github.com/bluss/arrayvec/pull/225/files
52
53        for (dir,           warn_if_missing ) in [
54            (root,          true            ),
55            (src.as_path(), false           ),
56            (dm.as_path(),  false           ),
57        ].iter().copied() {
58            let entries = if let Ok(e) = fs::read_dir(dir) {
59                e
60            } else if warn_if_missing {
61                println!("cargo:warning={:?} not readable, skipping in search for natvis files...", dir);
62                continue
63            } else {
64                // It is suprisingly common to manually specify the source path to flatten out / not to have a `src` dir
65                // (cloudabi, osmesa-sys, html5ever-atoms, gl_generator, percent-encoding, utf-8, fnv, smallvec, ...)
66                continue
67            };
68
69            println!("cargo:rerun-if-changed={}", dir.display());
70
71            for entry in entries {
72                let entry = if let Ok(e) = entry { e } else { continue };
73                let path = entry.path();
74                if !path.is_file() { continue }
75                let ext = if let Some(e) = path.extension().and_then(|os| os.to_str()) { e } else { continue };
76                if !ext.eq_ignore_ascii_case("natvis") { continue }
77
78                // Found a .natvis file, lets try to add it to link_args
79
80                let path = path.canonicalize();
81                let path = if let Some(p) = path.as_ref().ok().and_then(|p| p.to_str()) {
82                    if p.starts_with("\\\\?\\") { &p[4..] } else { p } // linker cannot handle UNC paths
83                } else {
84                    println!("cargo:warning={:?} cannot be canonicalized, skipping embedding this natvis file...", path);
85                    continue;
86                };
87
88                if link_args.len() != 0 {
89                    link_args.push(" ");
90                }
91                link_args.push("\"/NATVIS:");
92                link_args.push(path);
93                link_args.push("\"");
94                link_args_changed = true;
95                println!("cargo:rerun-if-changed={}", path);
96            }
97        }
98    }
99
100    if link_args_changed {
101        std::env::set_var("LINK", &link_args);
102        println!("cargo:rustc-env=LINK={}", link_args.to_string_lossy());
103    }
104}