1use anyhow::anyhow;
2use clap::Parser;
3use std::{collections::HashMap, path::PathBuf, process::Command};
4
5mod sys;
6use sys::*;
7
8const ENV_VAR_VC_LTL5: &str = "VC_LTL";
9const ENV_VAR_YY_THUNKS: &str = "YY_THUNKS";
10
11#[derive(Debug, Parser)]
13pub struct ThunkBuilder {
14 #[arg(short, long, value_name = "OS")]
16 os: Option<OS>,
17 #[arg(short, long)]
19 arch: Option<Arch>,
20 #[arg(long, value_name = "IS_LIB")]
22 lib: bool,
23 #[arg(short, long)]
25 subsystem: Option<Subsystem>,
26 #[arg(last = true, value_name = "CARGO_ARGS")]
28 cargo_args: Vec<String>,
29}
30
31impl ThunkBuilder {
32 pub fn build(mut self) -> anyhow::Result<Thunk> {
33 let env_vars: HashMap<String, String> = std::env::vars().collect();
34
35 let mut vc_ltl = {
36 let vc_ltl_env_path = env_vars.get(ENV_VAR_VC_LTL5).ok_or_else(|| {
37 anyhow!("You need to set {} environment variable.", ENV_VAR_VC_LTL5)
38 })?;
39 PathBuf::from(vc_ltl_env_path)
40 };
41
42 let os = self.os.unwrap_or(OS::Windows7);
43
44 let arch = if let Ok(arch_from_args) = get_arch_from_args(self.cargo_args.as_slice()) {
45 arch_from_args
46 } else {
47 let un_arch = self.arch.unwrap_or(get_default_arch()?);
48 let target = un_arch
49 .to_rust_target()
50 .ok_or_else(|| anyhow!("arch {} fail translate to target", un_arch.to_string()))?;
51 self.cargo_args.extend(["--target".to_owned(), target]);
52 un_arch
53 };
54
55 let os_lib =
56 get_vc_ltl_os_lib_path(os, arch).ok_or_else(|| anyhow!("os or arch is wrong"))?;
57
58 vc_ltl.push(os_lib);
59
60 let os_version =
61 get_os_version(os, arch).ok_or_else(|| anyhow!("failed to get os version"))?;
62
63 let is_lib = { get_is_lib_from_args(self.cargo_args.as_slice()) || self.lib };
64
65 let mut subsystem = Some(self.subsystem.unwrap_or(Subsystem::Console));
66
67 if is_lib {
68 subsystem = None;
69 }
70
71 let subsystem_args =
72 subsystem.map(|x| format!("-Clink-args=/SUBSYSTEM:{},{}", x.to_string(), os_version));
73
74 let mut rust_flags = vec!["-L".into(), format!("{}", vc_ltl.to_string_lossy())];
75
76 if let Some(args) = subsystem_args {
77 rust_flags.push(args.into());
78
79 if let Some(Subsystem::Windows) = subsystem {
80 rust_flags.push("-Clink-args=/ENTRY:mainCRTStartup".into())
81 }
82 }
83
84 let thunks_obj = {
85 let mut thunks = {
86 let yy_thunks_env_path = env_vars.get(ENV_VAR_YY_THUNKS).ok_or_else(|| {
87 anyhow!(
88 "You need to set {} environment variable.",
89 ENV_VAR_YY_THUNKS
90 )
91 })?;
92 PathBuf::from(yy_thunks_env_path)
93 };
94
95 let os_obj = get_yy_thunks_obj_path(os, arch).ok_or_else(|| anyhow!(""))?;
96 thunks.push(os_obj);
97 Some(thunks)
98 };
99
100 if let Some(obj) = thunks_obj {
101 rust_flags.push(format!("-Clink-args={}", obj.to_string_lossy()));
102 }
103
104 let target_dir = format!("./target/win{}_build", os.to_string().to_ascii_lowercase());
105
106 let mut cargo_args = vec![
107 "build".to_owned(),
108 "--target-dir".to_owned(),
109 target_dir.clone(),
110 ];
111
112 cargo_args.extend(self.cargo_args);
113
114 let thunk = Thunk {
115 rust_flags,
116 cargo_args,
117 os,
118 arch,
119 target_dir,
120 };
121
122 Ok(thunk)
123 }
124}
125
126#[derive(Debug)]
127pub struct Thunk {
128 rust_flags: Vec<String>,
129 cargo_args: Vec<String>,
130 os: OS,
131 arch: Arch,
132 target_dir: String,
133}
134
135impl Thunk {
136 pub fn run(self) {
137 let rust_flags = self.rust_flags.join(" ");
138 let cargo_args = self.cargo_args;
139
140 println!(
141 "Start to build for Windows {}({}) using VC-LTL and YY-Thunks: ",
142 self.os.to_string(),
143 self.arch.to_string(),
144 );
145 println!(" * RUSTFLAGS = {}", rust_flags);
146 println!(" * Command = cargo {}", cargo_args.join(" "));
147 println!("Cargo Output:");
148
149 let _status = Command::new("cargo")
150 .env("RUSTFLAGS", rust_flags)
151 .args(cargo_args)
152 .status()
153 .unwrap();
154
155 println!(
156 "You can find the builds in target directory: {}",
157 self.target_dir
158 );
159 }
160}