1use std::{fs, io::Cursor, os::unix::fs::PermissionsExt, path::Path};
2
3use crate::library::Library;
4
5use color_eyre::{Help, Result};
6
7pub struct Runner<'a, T: Library> {
9 version: &'a str,
11
12 ni_version: &'a str,
14
15 libraries: Vec<T>,
17
18 header_contents: &'a str,
20
21 allowlist: &'a str,
23
24 lib_list: &'a [&'a str],
26
27 output_directory: &'a Path,
30
31 clang_args: String,
33}
34
35impl<'a, T: Library> Runner<'a, T> {
36 pub fn new(
38 version: &'a str,
39 ni_version: &'a str,
40 libraries: Vec<T>,
41 header_contents: &'a str,
42 allowlist: &'a str,
43 lib_list: &'a [&'a str],
44 output_directory: &'a Path,
45 clang_args: String,
46 ) -> Self {
47 Self {
48 version,
49 ni_version,
50 libraries,
51 header_contents,
52 allowlist,
53 lib_list,
54 output_directory,
55 clang_args,
56 }
57 }
58
59 pub async fn run(&mut self, link_only: bool) -> Result<()> {
61 let complete_marker_path = self.output_directory.join("arfur.complete");
62
63 if !complete_marker_path.exists() && !link_only {
64 self.download_libraries()
65 .await
66 .note("Failed to download libraries.")?;
67
68 self.install_libraries()
69 .await
70 .note("Failed to install libraries.")?;
71
72 #[cfg(feature = "bindgen")]
73 self.generate_bindings()
74 .await
75 .note("Failed to generate bindings.")?;
76
77 self.cleanup().note("Failed to clean up after build.")?;
78 } else {
79 println!("Built copy found, not building again...");
80 }
81
82 self.link_libraries()
83 .note("Failed to ask Cargo to link to libraries.")?;
84
85 Ok(())
86 }
87
88 pub async fn download_libraries(&mut self) -> Result<()> {
92 let extracted_dir = self.output_directory.join("raw");
93
94 for library in &self.libraries {
95 let link = library.get_link(self.version, self.ni_version);
96
97 let zipped = reqwest::get(link)
98 .await
99 .note("Failed to download archive.")?
100 .bytes()
101 .await
102 .note("Failed to convert archive into bytes.")?;
103
104 zip_extract::extract(Cursor::new(zipped), &extracted_dir, false)
105 .note("Failed to extract zip file.")?;
106 }
107
108 Ok(())
109 }
110
111 pub async fn install_libraries(&mut self) -> Result<()> {
118 let dynamic_library_dir = self
119 .output_directory
120 .join("raw")
121 .join("linux")
122 .join("athena")
123 .join("shared");
124
125 fs::set_permissions(&dynamic_library_dir, fs::Permissions::from_mode(0o755))?;
126 for file in fs::read_dir(&dynamic_library_dir)? {
127 let file = file?;
128 fs::set_permissions(&file.path(), fs::Permissions::from_mode(0o755))?;
129
130 if file.file_name().to_str().unwrap().ends_with(".debug") {
131 fs::remove_file(file.path())?;
133 } else if !&file.file_name().to_str().unwrap().ends_with(".so") {
134 let name = file.file_name();
140 let mut name = name.to_str().unwrap().chars();
141 for _ in 1..8 {
142 name.next_back();
143 }
144 let name = name.as_str();
145 let mut new_name = file.path();
146 new_name.set_file_name(name);
147 fs::rename(file.path(), new_name)?;
148 }
149 }
150
151 Ok(())
152 }
153
154 pub fn link_libraries(&mut self) -> Result<()> {
156 let dynamic_library_dir = self
157 .output_directory
158 .join("raw")
159 .join("linux")
160 .join("athena")
161 .join("shared");
162
163 for lib in self.lib_list.iter() {
164 println!("cargo:rustc-link-lib=dylib={}", lib);
165 }
166
167 println!(
168 "cargo:rustc-link-search=native={dynamic_library_dir}",
169 dynamic_library_dir = dynamic_library_dir.to_str().unwrap()
170 );
171
172 Ok(())
173 }
174
175 #[cfg(feature = "bindgen")]
177 pub async fn generate_bindings(&mut self) -> Result<()> {
178 let raw_directory = self.output_directory.join("raw");
179
180 let bindings = bindgen::Builder::default()
181 .header_contents("runner-header", self.header_contents)
183 .parse_callbacks(Box::new(bindgen::CargoCallbacks))
184 .enable_cxx_namespaces()
185 .allowlist_function(self.allowlist)
186 .allowlist_type(self.allowlist)
187 .allowlist_var(self.allowlist)
188 .clang_arg(format!("-I{}", raw_directory.to_str().unwrap()))
189 .clang_arg(self.clang_args.clone())
190 .clang_arg("-std=c++17")
191 .clang_args(&["-x", "c++"]);
192
193 println!("clang command: {:?}", bindings.command_line_flags());
194
195 bindings
196 .generate()
197 .note("Failed to generate bindings...")?
198 .write_to_file(self.output_directory.join("bindings.rs"))
199 .note("Failed to write bindings file...")?;
200
201 Ok(())
202 }
203
204 pub fn cleanup(&mut self) -> Result<()> {
206 let complete_marker_path = self.output_directory.join("arfur.complete");
207
208 fs::File::create(complete_marker_path)?;
209
210 Ok(())
211 }
212}