1use crate::source::BuildSource;
2use std::ops::{Deref};
3use std::path::PathBuf;
4
5mod meson;
6pub use meson::*;
7use crate::util::{TemporaryPath, create_temporary_path, install_prefix, build_library_type, BuildLibraryTypeError};
8use std::cell::RefCell;
9use std::collections::hash_map::DefaultHasher;
10use std::hash::{Hash, Hasher};
11
12#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
13pub enum LibraryType {
14 Static,
15 Shared
16}
17
18impl ToString for LibraryType {
19 fn to_string(&self) -> String {
20 match self {
21 LibraryType::Shared => "dylib",
22 LibraryType::Static => "static"
23 }.to_owned()
24 }
25}
26
27#[derive(Ord, PartialOrd, Eq, PartialEq, Debug)]
28pub enum LinkSearchKind {
29 Dependency,
30 Crate,
31 Native,
32 Framework,
33 All
34}
35
36impl ToString for LinkSearchKind {
37 fn to_string(&self) -> String {
38 match self {
39 LinkSearchKind::Dependency => "dependency",
40 LinkSearchKind::Crate => "crate",
41 LinkSearchKind::Native => "native",
42 LinkSearchKind::Framework => "framework",
43 LinkSearchKind::All => "all"
44 }.to_owned()
45 }
46}
47
48#[derive(Debug)]
49pub enum BuildCreateError {
50 Unknown,
51 MissingName,
52 MissingSource,
53 Missing(String),
54 FailedToCreateBuildDirectory(std::io::Error),
55 InvalidEnvLibraryType(String),
56}
57
58#[derive(Debug)]
74pub struct BuildError {
75 step: String,
76 error: BuildStepError
77}
78
79impl BuildError {
80 pub fn pretty_format(&self) -> String {
81 let mut result = String::with_capacity(self.error.stdout.len() + self.error.stderr.len() + self.step.len() + self.error.detail.len() + 200);
82
83 result.push_str(format!("Build step \"{}\" errored: {}\n", &self.step, &self.error.detail).as_ref());
84 if !self.error.stdout.is_empty() {
85 result.push_str("----------------- Stdout -----------------\n");
86 result.push_str(&self.error.stdout);
87 }
88
89 if !self.error.stderr.is_empty() {
90 result.push_str("----------------- Stderr -----------------\n");
91 result.push_str(&self.error.stderr);
92 }
93
94 result
95 }
96}
97
98#[derive(Debug)]
99pub struct BuildStepError {
100 detail: String,
101
102 stdout: String,
103 stderr: String
104}
105
106impl BuildStepError {
107 pub fn new_simple<S>(detail: S) -> Self
108 where S: Into<String>
109 {
110 Self::new(detail.into(), String::new(), String::new())
111 }
112
113 pub fn new_io<S>(detail: S, error: std::io::Error) -> Self
114 where S: Into<String>
115 {
116 Self::new(detail.into(), String::new(), format!("IOError: {}", error.to_string()))
117 }
118
119 pub fn new(detail: String, stdout: String, stderr: String) -> Self {
120 BuildStepError{
121 detail,
122
123 stdout,
124 stderr
125 }
126 }
127
128 pub fn stdout(&self) -> &str {
129 &self.stdout
130 }
131
132 pub fn stderr(&self) -> &str {
133 &self.stderr
134 }
135}
136
137pub struct BuildLibrary {
138 name: String,
139 kind: Option<LibraryType>
140}
141
142impl ToString for BuildLibrary {
143 fn to_string(&self) -> String {
144 if let Some(kind) = &self.kind {
145 format!("{}={}", kind.to_string(), self.name).to_owned()
146 } else {
147 self.name.clone()
148 }
149 }
150}
151
152pub struct BuildLibraryPath {
153 path: PathBuf,
154 kind: LinkSearchKind
155}
156
157impl ToString for BuildLibraryPath {
158 fn to_string(&self) -> String {
159 if self.kind == LinkSearchKind::All {
160 self.path.to_string_lossy().into_owned()
161 } else {
162 format!("{}={}", self.kind.to_string(), self.path.to_string_lossy().into_owned()).to_owned()
163 }
164 }
165}
166
167pub struct BuildResult {
168 libraries: Vec<BuildLibrary>,
169 library_paths: Vec<BuildLibraryPath>,
170 custom_compiler_emits: Vec<String>
171}
172
173impl BuildResult {
174 pub fn new() -> Self {
175 BuildResult{
176 libraries: Vec::new(),
177 library_paths: Vec::new(),
178 custom_compiler_emits: Vec::new()
179 }
180 }
181
182 pub fn add_library(&mut self, name: String, kind: Option<LibraryType>) -> &mut Self {
183 self.libraries.push(BuildLibrary{ name, kind });
184 self
185 }
186
187 pub fn libraries(&self) -> &Vec<BuildLibrary> {
188 &self.libraries
189 }
190
191 pub fn add_library_path(&mut self, path: PathBuf, kind: Option<LinkSearchKind>) -> &mut Self {
192 self.library_paths.push(BuildLibraryPath{ path, kind: kind.unwrap_or(LinkSearchKind::All) });
194 self
195 }
196
197 pub fn library_paths(&self) -> &Vec<BuildLibraryPath> {
198 &self.library_paths
199 }
200
201 pub fn add_emit(&mut self, line: String) -> &mut Self {
202 self.custom_compiler_emits.push(line);
203 self
204 }
205
206 pub fn emit_cargo(&self) {
207 self.library_paths.iter().for_each(|path| {
208 println!("cargo:rustc-link-search={}", path.to_string());
209 });
210
211 self.libraries.iter().for_each(|path| {
212 println!("cargo:rustc-link-search={}", path.to_string());
213 });
214
215 self.custom_compiler_emits.iter().for_each(|emit| {
216 println!("cargo:{}", emit);
217 });
218 }
219}
220
221pub trait BuildStep {
222 fn name(&self) -> &str;
223
224 fn hash(&self, hasher: &mut Box<dyn Hasher>);
226
227 fn execute(&mut self, build: &Build, result: &mut BuildResult) -> Result<(), BuildStepError>;
229}
230
231pub struct Build {
232 name: String,
233 source: Box<dyn BuildSource>,
234 build_hash: u64,
235
236 steps: Vec<RefCell<Box<dyn BuildStep>>>,
237
238 library_type: LibraryType,
239
240 build_path: TemporaryPath,
241 install_prefix: Option<PathBuf>,
242}
243
244impl Build {
245 pub fn builder() -> BuildBuilder {
247 BuildBuilder::new()
248 }
249
250 pub fn name(&self) -> &str {
252 &self.name
253 }
254
255 pub fn library_type(&self) -> LibraryType {
257 self.library_type
258 }
259
260 pub fn install_prefix(&self) -> &Option<PathBuf> {
263 &self.install_prefix
264 }
265
266 pub fn build_path(&self) -> &PathBuf {
268 &self.build_path.deref()
269 }
270
271 pub fn source(&self) -> &Box<dyn BuildSource> {
272 &self.source
273 }
274
275 pub fn build_hash(&self) -> u64 {
276 self.build_hash
277 }
278
279 pub fn execute(&mut self) -> Result<BuildResult, BuildError> {
281 if let Err(error) = self.source.setup() {
282 return Err(BuildError{
283 step: "source setup".to_owned(),
284 error
285 });
286 }
287
288 let mut result = BuildResult::new();
289 for step in self.steps.iter() {
290 let mut step = RefCell::borrow_mut(step);
291
292 if let Err(err) = step.execute(self, &mut result) {
293 return Err(BuildError{
294 step: step.name().to_owned(),
295 error: err
296 })
297 }
298 }
299 Ok(result)
300 }
301}
302
303pub struct BuildBuilder {
304 name: Option<String>,
305 source: Option<Box<dyn BuildSource>>,
306
307 steps: Vec<RefCell<Box<dyn BuildStep>>>,
308 library_type: Option<LibraryType>,
309
310 install_prefix: Option<PathBuf>,
311 build_path: Option<PathBuf>,
312
313 remove_build_dir: bool,
315 }
317
318impl BuildBuilder {
319 fn new() -> Self {
320 BuildBuilder {
321 name: None,
322 source: None,
323
324 steps: Vec::new(),
325 library_type: None,
326
327 install_prefix: None,
328 build_path: None,
329
330 remove_build_dir: true
331 }
332 }
333
334 pub fn build(self) -> Result<Box<Build>, BuildCreateError> {
335 let name = if let Some(name) = self.name { name } else {
336 return Err(BuildCreateError::MissingName);
337 };
338 let source = if let Some(source) = self.source { source } else {
339 return Err(BuildCreateError::MissingSource);
340 };
341
342 let install_prefix = if let Some(prefix) = self.install_prefix {
343 Some(prefix)
344 } else if let Some(prefix) = install_prefix(&name) {
345 Some(prefix)
346 } else {
347 None
348 };
349
350 let library_type = if let Some(ltype) = self.library_type {
351 ltype
352 } else {
353 match build_library_type(&name) {
354 Ok(ltype) => ltype,
355 Err(BuildLibraryTypeError::InvalidValue(value)) => return Err(BuildCreateError::InvalidEnvLibraryType(value)),
356 Err(BuildLibraryTypeError::NotPresent) => LibraryType::Shared
357 }
358 };
359
360 let build_hash = {
361 let mut hash: Box<dyn Hasher> = Box::new(DefaultHasher::new());
362 name.hash(&mut hash);
363 source.hash(&mut hash);
364 install_prefix.hash(&mut hash);
365 library_type.hash(&mut hash);
366 self.steps.iter().enumerate().for_each(|(index, step)| {
367 let step = RefCell::borrow(step);
368 index.hash(&mut hash);
369 step.name().hash(&mut hash);
370 step.hash(&mut hash);
371 });
372 hash.finish()
373 };
374
375 let hash_str = base64::encode(build_hash.to_be_bytes()).replace("/", "_");
376 let build_path = match create_temporary_path(format!("build_{}_{}", &name, hash_str).as_ref(), self.build_path) {
377 Ok(path) => path,
378 Err(err) => return Err(BuildCreateError::FailedToCreateBuildDirectory(err))
379 };
380
381 if !self.remove_build_dir {
382 build_path.release();
383 }
384
385 Ok(Box::new(Build{
386 name,
387 source,
388 build_hash,
389
390 steps: self.steps,
391 library_type,
392
393 build_path,
394 install_prefix
395 }))
396 }
397
398 pub fn name<V>(mut self, value: V) -> Self
399 where V: Into<String>
400 {
401 self.name = Some(value.into());
402 self
403 }
404
405 pub fn source(mut self, source: Box<dyn BuildSource>) -> Self {
406 self.source = Some(source);
407 self
408 }
409
410 pub fn build_path(mut self, path: PathBuf) -> Self {
411 self.build_path = Some(path);
412 self
413 }
414
415 pub fn install_prefix(mut self, path: PathBuf) -> Self {
416 self.install_prefix = Some(path);
417 self
418 }
419
420 pub fn library_type(mut self, ltype: LibraryType) -> Self {
421 self.library_type = Some(ltype);
422 self
423 }
424
425 pub fn remove_build_dir(mut self, enabled: bool) -> Self {
426 self.remove_build_dir = enabled;
427 self
428 }
429
430 pub fn add_step(mut self, step: Box<dyn BuildStep>) -> Self {
431 self.steps.push(RefCell::new(step));
432 self
433 }
434}
435
436
437#[cfg(test)]
438mod test {
439 use crate::BuildStep;
440 use crate::build::{Build, BuildResult, BuildStepError};
441 use crate::source::{BuildSource};
442 use std::path::PathBuf;
443 use std::hash::Hasher;
444
445 struct DummyBuildStep { }
446 impl BuildStep for DummyBuildStep {
447 fn name(&self) -> &str {
448 "dummy"
449 }
450
451 fn hash(&self, _state: &mut Box<dyn Hasher>) { }
452
453 fn execute(&mut self, _build: &Build, _result: &mut BuildResult) -> Result<(), BuildStepError> {
454 Ok(())
455 }
456 }
457
458 struct DummyBuildSource {}
459 impl BuildSource for DummyBuildSource {
460 fn name(&self) -> &str {
461 "dummy"
462 }
463
464 fn hash(&self, _state: &mut Box<dyn Hasher>) { }
465
466 fn setup(&mut self) -> Result<(), BuildStepError> {
467 Ok(())
468 }
469
470 fn local_directory(&self) -> &PathBuf {
471 unimplemented!()
472 }
473
474 fn cleanup(&mut self) { }
475 }
476
477 #[test]
478 fn test_builder() {
479 let mut build = Build::builder()
480 .name("test")
481 .source(Box::new(DummyBuildSource{}))
482 .add_step(Box::new(DummyBuildStep{}))
483 .build().expect("failed to create dummy build");
484 build.execute().expect("build should have succeeded");
485 }
486}