1use std::env;
4use std::fs::remove_file;
5use std::io;
6use std::path::PathBuf;
7use std::process::Command;
8
9static CARGO_LINK_SEARCH: &str = "cargo:rustc-link-search=native=";
10static CARGO_LINK_LIB: &str = "cargo:rustc-link-lib=";
11
12pub fn link_lib(path: Option<&str>, name: &str) {
14 if let Some(path) = path {
15 println!("{}{}", CARGO_LINK_SEARCH, path);
16 }
17
18 println!("{}{}", CARGO_LINK_LIB, name);
19}
20
21pub fn link<P: AsRef<std::path::Path>>(filename: P) {
23 let mut filename = filename.as_ref().to_path_buf();
24 let name = filename.file_stem().expect("Invalid filename");
25 let s = String::from(name.to_str().expect("Invalid filename"));
26 let mut tmp: &str = &s;
27
28 if let Some(s) = s.strip_prefix("lib") {
29 tmp = &s[3..]
30 }
31
32 if s.ends_with(".a") {
33 tmp = &tmp[..tmp.len() - 2];
34 } else if s.ends_with(".so") {
35 tmp = &tmp[..tmp.len() - 3];
36 } else if s.ends_with(".dylib") {
37 tmp = &tmp[..tmp.len() - 6];
38 }
39
40 filename.pop();
41 link_lib(filename.to_str(), tmp);
42}
43
44pub fn compile_shared_library(
46 compiler: Option<&str>,
47 output: &str,
48 args: &[&str],
49) -> Result<bool, std::io::Error> {
50 let cxx = std::env::var("CXX").unwrap_or_else(|_| "c++".to_owned());
51 let mut cmd = Command::new(compiler.unwrap_or(&cxx));
52
53 cmd.arg("-std=c++17");
54 let res = cmd
55 .arg("-shared")
56 .arg("-o")
57 .arg(output)
58 .args(args)
59 .status()?;
60 Ok(res.success())
61}
62
63#[derive(Debug)]
65pub struct Build<'a> {
66 pub halide_path: PathBuf,
68
69 pub src: Vec<PathBuf>,
71
72 pub output: PathBuf,
74
75 pub cxx: Option<&'a str>,
77
78 pub cxxflags: Option<&'a str>,
80
81 pub ldflags: Option<&'a str>,
83
84 pub build_args: Vec<&'a str>,
86
87 pub run_args: Vec<&'a str>,
89
90 pub keep: bool,
92
93 pub generator: bool,
95}
96
97impl<'a> Build<'a> {
98 pub fn new<P: AsRef<std::path::Path>, Q: AsRef<std::path::Path>>(
100 halide_path: P,
101 output: Q,
102 ) -> Build<'a> {
103 Build {
104 halide_path: halide_path.as_ref().to_path_buf(),
105 src: vec![],
106 output: output.as_ref().to_path_buf(),
107 cxx: None,
108 cxxflags: None,
109 ldflags: None,
110 build_args: vec![],
111 run_args: vec![],
112 keep: false,
113 generator: false,
114 }
115 }
116
117 pub fn source_file(mut self, src: impl AsRef<std::path::Path>) -> Self {
118 self.src.push(src.as_ref().to_owned());
119 self
120 }
121
122 pub fn build_arg(mut self, src: &'a str) -> Self {
123 self.build_args.push(src.as_ref());
124 self
125 }
126
127 pub fn build_args(mut self, src: impl AsRef<[&'a str]>) -> Self {
128 self.build_args.extend(src.as_ref());
129 self
130 }
131
132 pub fn run_arg(mut self, src: &'a str) -> Self {
133 self.run_args.push(src.as_ref());
134 self
135 }
136
137 pub fn run_args(mut self, src: impl AsRef<[&'a str]>) -> Self {
138 self.run_args.extend(src.as_ref());
139 self
140 }
141
142 pub fn ldflags(mut self, flags: &'a str) -> Self {
143 self.ldflags = Some(flags);
144 self
145 }
146
147 pub fn cxxflags(mut self, flags: &'a str) -> Self {
148 self.cxxflags = Some(flags);
149 self
150 }
151
152 pub fn compiler(mut self, name: &'a str) -> Self {
153 self.cxx = Some(name);
154 self
155 }
156
157 pub fn keep(mut self, x: bool) -> Self {
158 self.keep = x;
159 self
160 }
161
162 pub fn generator(mut self, x: bool) -> Self {
163 self.generator = x;
164 self
165 }
166
167 pub fn build(&self) -> io::Result<bool> {
169 let cxx_default = env::var("CXX").unwrap_or_else(|_| "c++".to_string());
170 let mut cmd = Command::new(self.cxx.unwrap_or(cxx_default.as_str()));
171
172 cmd.arg("-std=c++17");
173 cmd.args(&["-I", &self.halide_path.join("include").to_string_lossy()])
174 .args(&["-I", &self.halide_path.join("tools").to_string_lossy()]);
175
176 if let Some(flags) = &self.cxxflags {
177 cmd.args(flags.split(' '));
178 }
179
180 if self.generator {
181 cmd.arg(
182 &self
183 .halide_path
184 .join("tools")
185 .join("GenGen.cpp")
186 .to_string_lossy()
187 .as_ref(),
188 );
189 }
190
191 cmd.args(&self.build_args);
192
193 let tinfo = std::env::var("TERMINFO").unwrap_or_else(|_| "-lncurses".to_string());
194
195 cmd.args(&self.src)
196 .args(&["-o", &self.output.to_string_lossy()])
197 .args(&[
198 "-L",
199 &self.halide_path.join("lib").to_string_lossy(),
200 "-lHalide",
201 "-lpng",
202 "-ljpeg",
203 "-lpthread",
204 &tinfo,
205 "-ldl",
206 "-lz",
207 ]);
208
209 if let Some(flags) = &self.ldflags {
210 cmd.args(flags.split(' '));
211 }
212
213 cmd.status().map(|status| status.success())
214 }
215
216 pub fn run(&self) -> io::Result<bool> {
218 if !self.output.exists() {
219 return Ok(false);
220 }
221
222 let res = Command::new(&self.output)
223 .args(&self.run_args)
224 .env("LD_LIBRARY_PATH", self.halide_path.join("lib"))
225 .status()
226 .map(|status| status.success());
227
228 if !self.keep {
229 let _ = remove_file(&self.output);
230 }
231
232 res
233 }
234}
235
236pub struct Source {
238 pub halide_path: PathBuf,
239 pub repo: String,
240 pub branch: String,
241 pub make: String,
242 pub make_flags: Vec<String>,
243}
244
245impl Source {
246 pub fn download(&self) -> io::Result<bool> {
248 Command::new("git")
249 .arg("clone")
250 .args(&["-b", self.branch.as_str()])
251 .arg(&self.repo)
252 .arg(&self.halide_path)
253 .status()
254 .map(|status| status.success())
255 }
256
257 pub fn update(&self) -> io::Result<bool> {
259 Command::new("git")
260 .current_dir(&self.halide_path)
261 .arg("pull")
262 .arg("origin")
263 .arg(&self.branch)
264 .status()
265 .map(|status| status.success())
266 }
267
268 pub fn build(&self) -> io::Result<bool> {
270 Command::new(&self.make)
271 .current_dir(&self.halide_path)
272 .args(&self.make_flags)
273 .status()
274 .map(|status| status.success())
275 }
276}