tugger_windows/
vc_redistributable.rs1use {
6 anyhow::{anyhow, Result},
7 once_cell::sync::Lazy,
8 std::{
9 fmt::{Display, Formatter},
10 path::PathBuf,
11 },
12 tugger_common::http::RemoteContent,
13};
14
15#[cfg(windows)]
16use {crate::find_vswhere, std::collections::BTreeMap};
17
18pub static VC_REDIST_X86: Lazy<RemoteContent> = Lazy::new(|| {
24 RemoteContent {
25 name: "VC_REDIST_X86".to_string(),
26 url: "https://download.visualstudio.microsoft.com/download/pr/888b4c07-c602-499a-9efb-411188496ce7/F3A86393234099BEDD558FD35AB538A6E4D9D4F99AD5ADFA13F603D4FF8A42DC/VC_redist.x86.exe".to_string(),
27 sha256: "f3a86393234099bedd558fd35ab538a6e4d9d4f99ad5adfa13f603d4ff8a42dc".to_string(),
28 }
29});
30
31pub static VC_REDIST_X64: Lazy<RemoteContent> = Lazy::new(|| {
32 RemoteContent {
33 name: "VC_REDIST_X64".to_string(),
34 url: "https://download.visualstudio.microsoft.com/download/pr/36e45907-8554-4390-ba70-9f6306924167/97CC5066EB3C7246CF89B735AE0F5A5304A7EE33DC087D65D9DFF3A1A73FE803/VC_redist.x64.exe".to_string(),
35 sha256: "97cc5066eb3c7246cf89b735ae0f5a5304a7ee33dc087d65d9dff3a1a73fe803".to_string(),
36 }
37});
38
39pub static VC_REDIST_ARM64: Lazy<RemoteContent> = Lazy::new(|| {
40 RemoteContent {
41 name: "VC_REDIST_ARM64".to_string(),
42 url: "https://download.visualstudio.microsoft.com/download/pr/888b4c07-c602-499a-9efb-411188496ce7/B76EF09CD8B114148EADDDFC6846EF178E6B7797F590191E22CEE29A20B51692/VC_redist.arm64.exe".to_string(),
43 sha256: "b76ef09cd8b114148eadddfc6846ef178e6b7797f590191e22cee29a20b51692".to_string(),
44 }
45});
46
47#[derive(Debug, PartialEq, Eq)]
49pub enum VcRedistributablePlatform {
50 X86,
51 X64,
52 Arm64,
53}
54
55impl Display for VcRedistributablePlatform {
56 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
57 f.write_str(match self {
58 Self::X86 => "x86",
59 Self::X64 => "x64",
60 Self::Arm64 => "arm64",
61 })
62 }
63}
64
65impl TryFrom<&str> for VcRedistributablePlatform {
66 type Error = anyhow::Error;
67
68 fn try_from(value: &str) -> anyhow::Result<Self, Self::Error> {
69 match value {
70 "x86" => Ok(Self::X86),
71 "x64" => Ok(Self::X64),
72 "arm64" => Ok(Self::Arm64),
73 _ => Err(anyhow!(
74 "{} is not a valid platform; use 'x86', 'x64', or 'arm64'",
75 value
76 )),
77 }
78 }
79}
80
81#[cfg(windows)]
89pub fn find_visual_cpp_redistributable(
90 redist_version: &str,
91 platform: VcRedistributablePlatform,
92) -> Result<Vec<PathBuf>> {
93 let vswhere_exe = find_vswhere()?;
94
95 let cmd = duct::cmd(
96 vswhere_exe,
97 vec![
98 "-products".to_string(),
99 "*".to_string(),
100 "-requires".to_string(),
101 format!("Microsoft.VisualCPP.Redist.{}.Latest", redist_version),
102 "-latest".to_string(),
103 "-property".to_string(),
104 "installationPath".to_string(),
105 "-utf8".to_string(),
106 ],
107 )
108 .stdout_capture()
109 .stderr_capture()
110 .run()?;
111
112 let install_path = PathBuf::from(
113 String::from_utf8(cmd.stdout)?
114 .strip_suffix("\r\n")
115 .ok_or_else(|| anyhow!("unable to strip string"))?,
116 );
117
118 let paths = glob::glob(
122 &install_path
123 .join(format!(
124 "VC/Redist/MSVC/{}.*/{}/Microsoft.VC*.CRT/vcruntime*.dll",
125 redist_version, platform
126 ))
127 .display()
128 .to_string(),
129 )?
130 .collect::<Vec<_>>()
131 .into_iter()
132 .map(|r| r.map_err(|e| anyhow!("glob error: {}", e)))
133 .collect::<Result<Vec<PathBuf>>>()?;
134
135 let mut paths_by_version: BTreeMap<semver::Version, Vec<PathBuf>> = BTreeMap::new();
136
137 for path in paths {
138 let stripped = path.strip_prefix(install_path.join("VC").join("Redist").join("MSVC"))?;
139 let mut components = stripped.components();
142 let version_path = components.next().ok_or_else(|| {
143 anyhow!("unable to determine version component (this should not happen)")
144 })?;
145
146 paths_by_version
147 .entry(semver::Version::parse(
148 version_path.as_os_str().to_string_lossy().as_ref(),
149 )?)
150 .or_insert_with(Vec::new)
151 .push(path);
152 }
153
154 Ok(paths_by_version
155 .into_iter()
156 .last()
157 .ok_or_else(|| anyhow!("unable to find install VC++ Redistributable"))?
158 .1)
159}
160
161#[cfg(unix)]
162pub fn find_visual_cpp_redistributable(
163 _version: &str,
164 _platform: VcRedistributablePlatform,
165) -> Result<Vec<PathBuf>> {
166 Err(anyhow!(
168 "Finding the Visual C++ Redistributable is not supported outside of Windows"
169 ))
170}
171
172#[cfg(test)]
173mod tests {
174 use {
175 super::*,
176 tugger_common::{http::download_to_path, testutil::*},
177 };
178
179 #[test]
180 fn test_vcredist_download() -> Result<()> {
181 download_to_path(
182 &VC_REDIST_X86,
183 DEFAULT_DOWNLOAD_DIR.join("vc_redist.x86.exe"),
184 )?;
185 download_to_path(
186 &VC_REDIST_X64,
187 DEFAULT_DOWNLOAD_DIR.join("vc_redist.x64.exe"),
188 )?;
189 download_to_path(
190 &VC_REDIST_ARM64,
191 DEFAULT_DOWNLOAD_DIR.join("vc_redist.arm64.exe"),
192 )?;
193
194 Ok(())
195 }
196
197 #[test]
198 fn test_find_visual_cpp_redistributable_14() {
199 let platforms = vec![
200 VcRedistributablePlatform::X86,
201 VcRedistributablePlatform::X64,
202 VcRedistributablePlatform::Arm64,
203 ];
204
205 for platform in platforms {
206 let res = find_visual_cpp_redistributable("14", platform);
207
208 if cfg!(windows) {
209 if res.is_ok() {
210 println!("found vcruntime files: {:?}", res.unwrap());
211 }
212 } else {
213 assert!(res.is_err());
214 }
215 }
216 }
217}