├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── shell.nix └── src ├── driver.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "cargo-typesize" 7 | version = "0.2.0" 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-typesize" 3 | version = "0.2.0" 4 | edition = "2021" 5 | description = "Cargo extension to list size of all types in a crate" 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | 11 | [[bin]] 12 | name = "cargo-typesize" 13 | test = false 14 | path = "src/main.rs" 15 | 16 | [[bin]] 17 | name = "typesize-driver" 18 | path = "src/driver.rs" 19 | 20 | [dependencies] 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cargo typesize 2 | 3 | List the size of all types in a Rust crate 4 | 5 | ## Install + Usage 6 | 7 | Cargo typesize depends on nightly for building and running, this is a restriction imposed by rustc to prevent linking against the rustc compiler on stable. 8 | 9 | ``` 10 | # Add the necessary components to the nightly compiler 11 | rustup default nightly 12 | rustup component add rust-src rustc-dev llvm-tools-preview 13 | ``` 14 | 15 | ``` 16 | # Install cargo-typesize using the nightly compiler 17 | cargo +nightly install cargo-typesize 18 | ``` 19 | 20 | Then make sure all invocations are done under nightly: 21 | 22 | ``` 23 | cargo +nightly typesize 24 | ``` 25 | 26 | 27 | ## Sample output 28 | Output of running `cargo typesize` on [sn_consensus](https://github.com/maidsafe/sn_consensus) 29 | 30 | ``` 31 | Inspecting layout of lib: sn_consensus 32 | 0 () - src/decision.rs:9:56: 9:65 (#247) 33 | 0 () - src/decision.rs:9:67: 9:78 (#258) 34 | 0 () - src/fault.rs:19:56: 19:65 (#340) 35 | 0 () - src/fault.rs:19:67: 19:78 (#353) 36 | 0 () - src/sn_membership.rs:23:55: 23:64 (#408) 37 | 0 () - src/sn_membership.rs:23:66: 23:77 (#411) 38 | 0 () - src/vote.rs:13:49: 13:58 (#458) 39 | 0 () - src/vote.rs:13:60: 13:71 (#467) 40 | 0 () - src/vote.rs:186:49: 186:58 (#545) 41 | 0 () - src/vote.rs:186:60: 186:71 (#556) 42 | 0 () - src/vote.rs:88:49: 88:58 (#499) 43 | 0 () - src/vote.rs:88:60: 88:71 (#510) 44 | 0 for fault::Fault>::deserialize::__Visitor<'de, T> as decision::_::_serde::de::Visitor<'de>>::visit_enum::__FieldVisitor - src/fault.rs:19:67: 19:78 (#353) 45 | 0 for fault::Fault>::deserialize::__Visitor<'de, T> as decision::_::_serde::de::Visitor<'de>>::visit_enum::__Visitor<'de, T> - src/fault.rs:19:67: 19:78 (#353) 46 | 0 for vote::Ballot>::deserialize::__Visitor<'de, T> as decision::_::_serde::de::Visitor<'de>>::visit_enum::__FieldVisitor - src/vote.rs:13:60: 13:71 (#467) 47 | 0 for vote::Ballot>::deserialize::__Visitor<'de, T> as decision::_::_serde::de::Visitor<'de>>::visit_enum::__Visitor<'de, T> - src/vote.rs:13:60: 13:71 (#467) 48 | 0 decision::_:: for decision::Decision>::deserialize::__FieldVisitor - src/decision.rs:9:67: 9:78 (#258) 49 | 0 decision::_:: for decision::Decision>::deserialize::__Visitor<'de, T> - src/decision.rs:9:67: 9:78 (#258) 50 | 0 fault::_:: for fault::Fault>::deserialize::__FieldVisitor - src/fault.rs:19:67: 19:78 (#353) 51 | 0 fault::_:: for fault::Fault>::deserialize::__Visitor<'de, T> - src/fault.rs:19:67: 19:78 (#353) 52 | 0 sn_membership::_:: for sn_membership::Reconfig>::deserialize::__FieldVisitor - src/sn_membership.rs:23:66: 23:77 (#411) 53 | 0 sn_membership::_:: for sn_membership::Reconfig>::deserialize::__Visitor<'de, T> - src/sn_membership.rs:23:66: 23:77 (#411) 54 | 0 vote::_:: for vote::Ballot>::deserialize::__FieldVisitor - src/vote.rs:13:60: 13:71 (#467) 55 | 0 vote::_:: for vote::Ballot>::deserialize::__Visitor<'de, T> - src/vote.rs:13:60: 13:71 (#467) 56 | 0 vote::_:: for vote::SignedVote>::deserialize::__FieldVisitor - src/vote.rs:186:60: 186:71 (#556) 57 | 0 vote::_:: for vote::SignedVote>::deserialize::__Visitor<'de, T> - src/vote.rs:186:60: 186:71 (#556) 58 | 0 vote::_:: for vote::Vote>::deserialize::__FieldVisitor - src/vote.rs:88:60: 88:71 (#510) 59 | 0 vote::_:: for vote::Vote>::deserialize::__Visitor<'de, T> - src/vote.rs:88:60: 88:71 (#510) 60 | 1 for fault::Fault>::deserialize::__Visitor<'de, T> as decision::_::_serde::de::Visitor<'de>>::visit_enum::__Field - src/fault.rs:19:67: 19:78 (#353) 61 | 1 for vote::Ballot>::deserialize::__Visitor<'de, T> as decision::_::_serde::de::Visitor<'de>>::visit_enum::__Field - src/vote.rs:13:60: 13:71 (#467) 62 | 1 decision::_:: for decision::Decision>::deserialize::__Field - src/decision.rs:9:67: 9:78 (#258) 63 | 1 fault::FaultError - src/fault.rs:8:1: 17:2 (#0) 64 | 1 fault::_:: for fault::Fault>::deserialize::__Field - src/fault.rs:19:67: 19:78 (#353) 65 | 1 sn_membership::_:: for sn_membership::Reconfig>::deserialize::__Field - src/sn_membership.rs:23:66: 23:77 (#411) 66 | 1 u8 - src/lib.rs:37:1: 37:22 (#0) 67 | 1 vote::_:: for vote::Ballot>::deserialize::__Field - src/vote.rs:13:60: 13:71 (#467) 68 | 1 vote::_:: for vote::SignedVote>::deserialize::__Field - src/vote.rs:186:60: 186:71 (#556) 69 | 1 vote::_:: for vote::Vote>::deserialize::__Field - src/vote.rs:88:60: 88:71 (#510) 70 | 8 u64 - src/sn_handover.rs:11:1: 11:32 (#0) 71 | 8 u64 - src/sn_membership.rs:13:1: 13:27 (#0) 72 | 8 usize - src/sn_membership.rs:12:1: 12:35 (#0) 73 | 16 &'static [&'static str] - src/decision.rs:9:67: 9:78 (#258) 74 | 16 &'static [&'static str] - src/fault.rs:19:67: 19:78 (#353) 75 | 16 &'static [&'static str] - src/sn_membership.rs:23:66: 23:77 (#411) 76 | 16 &'static [&'static str] - src/vote.rs:13:60: 13:71 (#467) 77 | 16 &'static [&'static str] - src/vote.rs:186:60: 186:71 (#556) 78 | 16 &'static [&'static str] - src/vote.rs:88:60: 88:71 (#510) 79 | 24 error::Error - src/error.rs:7:1: 62:2 (#0) 80 | 32 vote_count::SuperMajorityCount - src/vote_count.rs:60:1: 64:2 (#0) 81 | 48 vote_count::Candidate - src/vote_count.rs:12:1: 17:2 (#0) 82 | 72 vote_count::VoteCount - src/vote_count.rs:67:1: 71:2 (#0) 83 | 336 decision::Decision - src/decision.rs:10:1: 14:2 (#0) 84 | 488 consensus::Consensus - src/consensus.rs:12:1: 20:2 (#0) 85 | 496 sn_handover::Handover - src/sn_handover.rs:14:1: 17:2 (#0) 86 | 544 sn_membership::Membership - src/sn_membership.rs:16:1: 21:2 (#0) 87 | ``` 88 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | moz_overlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz); 3 | nixpkgs = import { 4 | overlays = [ moz_overlay ]; 5 | }; 6 | ruststable = (nixpkgs.latest.rustChannels.nightly.rust.override { 7 | extensions = [ "rust-src" "rust-analysis" ];} 8 | ); 9 | in 10 | with nixpkgs; 11 | stdenv.mkDerivation { 12 | name = "rust"; 13 | buildInputs = [ 14 | rustup ruststable 15 | 16 | 17 | # for sn_node_gui experiments 18 | 19 | xorg.libX11 20 | xorg.libXcursor 21 | xorg.libXrandr 22 | xorg.libXi 23 | pkg-config 24 | ]; 25 | 26 | # For sn_node_gui experiments 27 | 28 | APPEND_LIBRARY_PATH = "${lib.makeLibraryPath [ libGL ]}"; 29 | shellHook = '' 30 | export LD_LIBRARY_PATH="$APPEND_LIBRARY_PATH:$LD_LIBRARY_PATH" 31 | ''; 32 | } 33 | -------------------------------------------------------------------------------- /src/driver.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] 2 | #![cfg_attr(feature = "deny-warnings", deny(warnings))] 3 | // warn on lints, that are included in `rust-lang/rust`s bootstrap 4 | #![warn(rust_2018_idioms, unused_lifetimes)] 5 | // warn on rustc internal lints 6 | #![warn(rustc::internal)] 7 | 8 | // FIXME: switch to something more ergonomic here, once available. 9 | // (Currently there is no way to opt into sysroot crates without `extern crate`.) 10 | extern crate rustc_driver; 11 | extern crate rustc_hir; 12 | extern crate rustc_interface; 13 | extern crate rustc_middle; 14 | extern crate rustc_session; 15 | extern crate rustc_target; 16 | 17 | use rustc_driver::Compilation; 18 | use rustc_hir::Item; 19 | use rustc_interface::{interface, Queries}; 20 | use rustc_middle::ty::Ty; 21 | use rustc_session::config::ErrorOutputType; 22 | use rustc_session::EarlyDiagCtxt; 23 | use rustc_target::abi; 24 | 25 | use std::collections::BTreeMap; 26 | use std::env; 27 | use std::ops::Deref; 28 | use std::path::{Path, PathBuf}; 29 | use std::process::{exit, Command}; 30 | 31 | /// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If 32 | /// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`. 33 | fn arg_value<'a, T: Deref>( 34 | args: &'a [T], 35 | find_arg: &str, 36 | pred: impl Fn(&str) -> bool, 37 | ) -> Option<&'a str> { 38 | let mut args = args.iter().map(Deref::deref); 39 | while let Some(arg) = args.next() { 40 | let mut arg = arg.splitn(2, '='); 41 | if arg.next() != Some(find_arg) { 42 | continue; 43 | } 44 | 45 | match arg.next().or_else(|| args.next()) { 46 | Some(v) if pred(v) => return Some(v), 47 | _ => {} 48 | } 49 | } 50 | None 51 | } 52 | 53 | #[test] 54 | fn test_arg_value() { 55 | let args = &["--bar=bar", "--foobar", "123", "--foo"]; 56 | 57 | assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); 58 | assert_eq!(arg_value(args, "--bar", |_| false), None); 59 | assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); 60 | assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); 61 | assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); 62 | assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); 63 | assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); 64 | assert_eq!( 65 | arg_value(args, "--foobar", |p| p.contains("12")), 66 | Some("123") 67 | ); 68 | assert_eq!(arg_value(args, "--foo", |_| true), None); 69 | } 70 | 71 | struct DefaultCallbacks; 72 | impl rustc_driver::Callbacks for DefaultCallbacks {} 73 | 74 | #[derive(Default)] 75 | struct TypeSize { 76 | sizes: BTreeMap, 77 | } 78 | 79 | impl TypeSize { 80 | fn add_type(&mut self, ty: Ty<'_>, item: &Item<'_>, layout: abi::Layout<'_>) { 81 | self.sizes 82 | .insert(format!("{ty:?} - {:?}", item.span), layout.size().bytes()); 83 | } 84 | 85 | fn print_typesizes(&self) { 86 | let mut sorted = Vec::from_iter(self.sizes.iter()); 87 | sorted.sort_by_key(|(name, bytes)| (*bytes, name.clone())); 88 | 89 | if let Ok(bin) = env::var("CARGO_BIN_NAME") { 90 | println!("Inspecting layout of bin: {bin}"); 91 | } else if let Ok(lib) = env::var("CARGO_PKG_NAME") { 92 | println!("Inspecting layout of lib: {lib}"); 93 | } 94 | 95 | let max_bytes_len = if let Some((_, largest)) = sorted.last() { 96 | format!("{largest}").len() 97 | } else { 98 | 0 99 | }; 100 | 101 | for (name, bytes) in sorted { 102 | let bytes_str = format!("{bytes}"); 103 | let pad = 104 | String::from_iter(std::iter::repeat(" ").take(max_bytes_len - bytes_str.len())); 105 | println!("{pad}{bytes_str}\t{name}"); 106 | } 107 | } 108 | } 109 | 110 | impl rustc_driver::Callbacks for TypeSize { 111 | fn after_analysis<'tcx>( 112 | &mut self, 113 | _compiler: &interface::Compiler, 114 | queries: &'tcx Queries<'tcx>, 115 | ) -> Compilation { 116 | if !self.sizes.is_empty() { 117 | panic!("Already computed sizes"); 118 | } 119 | // Analyze the program and inspect the types of definitions. 120 | queries.global_ctxt().unwrap().enter(|tcx| { 121 | for id in tcx.hir().items() { 122 | let hir = tcx.hir(); 123 | let item = hir.item(id); 124 | 125 | use rustc_hir::ItemKind; 126 | match item.kind { 127 | ItemKind::GlobalAsm(..) 128 | | ItemKind::Static(..) 129 | | ItemKind::Const(..) 130 | | ItemKind::TyAlias(..) 131 | | ItemKind::OpaqueTy(..) 132 | | ItemKind::Enum(..) 133 | | ItemKind::Struct(..) 134 | | ItemKind::Union(..) => (), 135 | _ => continue, 136 | } 137 | 138 | let ty = tcx.type_of(item.owner_id.def_id); 139 | let ty = ty.instantiate_identity(); 140 | let param_env = tcx.param_env(item.owner_id.def_id); 141 | match tcx.layout_of(param_env.and(ty)) { 142 | Ok(layout) => { 143 | self.add_type(ty, item, layout.layout); 144 | } 145 | Err(_) => continue, 146 | } 147 | } 148 | }); 149 | 150 | Compilation::Stop 151 | } 152 | } 153 | 154 | fn display_help() { 155 | println!( 156 | "\ 157 | Prints the size of every type in a Rust crate. 158 | 159 | Usage: 160 | cargo typesize [options] [--] [...] 161 | 162 | Common options: 163 | -h, --help Print this message 164 | --rustc Pass all args to rustc 165 | " 166 | ); 167 | } 168 | 169 | fn toolchain_path(home: Option, toolchain: Option) -> Option { 170 | home.and_then(|home| { 171 | toolchain.map(|toolchain| { 172 | let mut path = PathBuf::from(home); 173 | path.push("toolchains"); 174 | path.push(toolchain); 175 | path 176 | }) 177 | }) 178 | } 179 | 180 | fn read_sys_root(sys_root_arg: &Option<&str>) -> String { 181 | // Get the sysroot, looking from most specific to this invocation to the least: 182 | // - command line 183 | // - runtime environment 184 | // - SYSROOT 185 | // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN 186 | // - sysroot from rustc in the path 187 | // - compile-time environment 188 | // - SYSROOT 189 | // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN 190 | 191 | sys_root_arg 192 | .map(PathBuf::from) 193 | .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from)) 194 | .or_else(|| { 195 | let home = std::env::var("RUSTUP_HOME") 196 | .or_else(|_| std::env::var("MULTIRUST_HOME")) 197 | .ok(); 198 | let toolchain = std::env::var("RUSTUP_TOOLCHAIN") 199 | .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN")) 200 | .ok(); 201 | toolchain_path(home, toolchain) 202 | }) 203 | .or_else(|| { 204 | Command::new("rustc") 205 | .arg("--print") 206 | .arg("sysroot") 207 | .output() 208 | .ok() 209 | .and_then(|out| String::from_utf8(out.stdout).ok()) 210 | .map(|s| PathBuf::from(s.trim())) 211 | }) 212 | .or_else(|| option_env!("SYSROOT").map(PathBuf::from)) 213 | .or_else(|| { 214 | let home = option_env!("RUSTUP_HOME") 215 | .or(option_env!("MULTIRUST_HOME")) 216 | .map(ToString::to_string); 217 | let toolchain = option_env!("RUSTUP_TOOLCHAIN") 218 | .or(option_env!("MULTIRUST_TOOLCHAIN")) 219 | .map(ToString::to_string); 220 | toolchain_path(home, toolchain) 221 | }) 222 | .map(|pb| pb.to_string_lossy().to_string()) 223 | .expect( 224 | "need to specify SYSROOT env var during clippy compilation, or use rustup or multirust", 225 | ) 226 | } 227 | 228 | pub fn main() { 229 | let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); 230 | rustc_driver::init_rustc_env_logger(&early_dcx); 231 | exit(rustc_driver::catch_with_exit_code(move || { 232 | let mut orig_args: Vec = env::args().collect(); 233 | let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true); 234 | let have_sys_root_arg = sys_root_arg.is_some(); 235 | 236 | let sys_root = read_sys_root(&sys_root_arg); 237 | 238 | // make "typesize-driver --rustc" work like a subcommand that passes further args to "rustc" 239 | // for example `typesize-driver --rustc --version` will print the rustc version that typesize-driver 240 | // uses 241 | if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") { 242 | orig_args.remove(pos); 243 | orig_args[0] = "rustc".to_string(); 244 | 245 | // if we call "rustc", we need to pass --sysroot here as well 246 | let mut args: Vec = orig_args.clone(); 247 | if !have_sys_root_arg { 248 | args.extend(vec!["--sysroot".into(), sys_root]); 249 | }; 250 | 251 | return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run(); 252 | } 253 | // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. 254 | // We're invoking the compiler programmatically, so we ignore this/ 255 | let wrapper_mode = 256 | orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref()); 257 | 258 | if wrapper_mode { 259 | // we still want to be able to invoke it normally though 260 | orig_args.remove(1); 261 | } 262 | 263 | if !wrapper_mode 264 | && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) 265 | { 266 | display_help(); 267 | exit(0); 268 | } 269 | 270 | // this conditional check for the --sysroot flag is there so users can call 271 | // `typesize_driver` directly 272 | // without having to pass --sysroot or anything 273 | let mut args: Vec = orig_args.clone(); 274 | if !have_sys_root_arg { 275 | args.extend(vec!["--sysroot".into(), sys_root]); 276 | }; 277 | 278 | if env::var("CARGO_PRIMARY_PACKAGE").is_ok() { 279 | let mut typesize = TypeSize::default(); 280 | rustc_driver::RunCompiler::new(&args, &mut typesize).run()?; 281 | 282 | typesize.print_typesizes(); 283 | } 284 | 285 | // We always run the compiler again in the default mode after analysis. 286 | // 287 | // This is done to support cases where we are analyzing multiple interdependent 288 | // crates at once (e.g. in a workspace). 289 | // 290 | // Crates earlier in the compilation pipeline need to produce build artifacts 291 | // for dependent crates to compile correctly. 292 | // 293 | // There's probably a way to achieve this with a single compiler pass... 294 | rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run() 295 | })) 296 | } 297 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "deny-warnings", deny(warnings))] 2 | // warn on lints, that are included in `rust-lang/rust`s bootstrap 3 | #![warn(rust_2018_idioms, unused_lifetimes)] 4 | 5 | use std::env; 6 | use std::path::PathBuf; 7 | use std::process::{self, Command}; 8 | 9 | const CARGO_TYPESIZE_HELP: &str = r#"Lists type sizes for all types in a package. 10 | 11 | Usage: 12 | cargo typesize [options] [--] [...] 13 | 14 | Options: 15 | build Run with cargo build 16 | check Run with cargo check (default) 17 | test Run with cargo test 18 | -h, --help Print this message 19 | -V, --version Print version info and exit 20 | "#; 21 | 22 | fn show_help() { 23 | println!("{}", CARGO_TYPESIZE_HELP); 24 | } 25 | 26 | fn show_version() { 27 | println!("{} {}", env!("CARGO_BIN_NAME"), env!("CARGO_PKG_VERSION")); 28 | } 29 | 30 | pub fn main() { 31 | if env::args().any(|a| a == "--help" || a == "-h") { 32 | show_help(); 33 | return; 34 | } 35 | 36 | if env::args().any(|a| a == "--version" || a == "-V") { 37 | show_version(); 38 | return; 39 | } 40 | 41 | if let Err(code) = process(env::args().skip(2)) { 42 | process::exit(code); 43 | } 44 | } 45 | 46 | struct TypeSizeCmd { 47 | cargo_subcommand: &'static str, 48 | args: Vec, 49 | typesize_args: Vec, 50 | } 51 | 52 | impl TypeSizeCmd { 53 | fn new(old_args: impl Iterator) -> Self { 54 | let mut cargo_subcommand = "check"; 55 | let mut args = vec![]; 56 | let typesize_args: Vec = vec![]; 57 | 58 | for arg in old_args { 59 | match arg.as_str() { 60 | "build" => { 61 | cargo_subcommand = "build"; 62 | continue; 63 | } 64 | "check" => { 65 | cargo_subcommand = "check"; 66 | continue; 67 | } 68 | "test" => { 69 | cargo_subcommand = "test"; 70 | args.push("--no-run".to_string()); 71 | continue; 72 | } 73 | "--" => break, 74 | _ => {} 75 | } 76 | args.push(arg); 77 | } 78 | 79 | Self { 80 | cargo_subcommand, 81 | args, 82 | typesize_args, 83 | } 84 | } 85 | 86 | fn path() -> PathBuf { 87 | let mut path = env::current_exe() 88 | .expect("current executable path invalid") 89 | .with_file_name("typesize-driver"); 90 | 91 | if cfg!(windows) { 92 | path.set_extension("exe"); 93 | } 94 | 95 | path 96 | } 97 | 98 | fn into_std_cmd(self) -> Command { 99 | let mut cmd = Command::new("cargo"); 100 | let typesize_args: String = self 101 | .typesize_args 102 | .iter() 103 | .map(|arg| format!("{}__TYPESIZE_HACKERY__", arg)) 104 | .collect(); 105 | 106 | cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path()) 107 | .env("TYPESIZE_ARGS", typesize_args) 108 | .arg(self.cargo_subcommand) 109 | .args(&self.args); 110 | 111 | cmd 112 | } 113 | } 114 | 115 | fn process(old_args: I) -> Result<(), i32> 116 | where 117 | I: Iterator, 118 | { 119 | let cmd = TypeSizeCmd::new(old_args); 120 | 121 | let mut cmd = cmd.into_std_cmd(); 122 | 123 | let exit_status = cmd 124 | .spawn() 125 | .expect("could not run cargo") 126 | .wait() 127 | .expect("failed to wait for cargo?"); 128 | 129 | if exit_status.success() { 130 | Ok(()) 131 | } else { 132 | Err(exit_status.code().unwrap_or(-1)) 133 | } 134 | } 135 | --------------------------------------------------------------------------------