ptx_builder/
reporter.rs

1use std::fmt;
2use std::process::exit;
3
4use colored::*;
5use failure::Fail;
6
7use crate::builder::{BuildStatus, Builder};
8use crate::error::*;
9
10/// Cargo integration adapter.
11///
12/// Provides PTX assembly path to Rust through specified environment variable name
13/// and informs Cargo about device crate dependencies, so it can rebuild on changes.
14///
15/// # Usage in `build.rs`
16/// ```no_run
17/// use ptx_builder::error::Result;
18/// use ptx_builder::prelude::*;
19///
20/// fn main() -> Result<()> {
21///     CargoAdapter::with_env_var("PTX_PATH").build(Builder::new(".")?);
22/// }
23/// ```
24pub struct CargoAdapter {
25    env_name: String,
26}
27
28impl CargoAdapter {
29    /// Creates an instance of the adapter that will provide PTX assembly path
30    /// to Rust via `env_name` environment variable.
31    ///
32    /// The PTX assembly can later be used **in host crate**:
33    /// ```ignore
34    /// use std::ffi::CString;
35    ///
36    /// let ptx = CString::new(include_str!(env!("PTX_PATH")))?;
37    /// ```
38    pub fn with_env_var<S: AsRef<str>>(env_name: S) -> Self {
39        CargoAdapter {
40            env_name: env_name.as_ref().to_string(),
41        }
42    }
43
44    /// Runs build process and reports artifacts to Cargo.
45    ///
46    /// Depends on whether the build was successful or not, will either
47    /// call `exit(0)` or `exit(1)` and print error log to `stderr`.
48    #[allow(clippy::needless_pass_by_value)]
49    pub fn build(&self, builder: Builder) -> ! {
50        if let Err(error) = self.build_inner(&builder) {
51            eprintln!("{}", ErrorLogPrinter::print(error));
52            exit(1);
53        } else {
54            exit(0);
55        }
56    }
57
58    fn build_inner(&self, builder: &Builder) -> Result<()> {
59        match builder.build()? {
60            BuildStatus::Success(output) => {
61                let dependencies = output.dependencies()?;
62
63                println!(
64                    "cargo:rustc-env={}={}",
65                    self.env_name,
66                    output.get_assembly_path().display()
67                );
68
69                for path in dependencies {
70                    println!("cargo:rerun-if-changed={}", path.display());
71                }
72            }
73
74            BuildStatus::NotNeeded => {
75                println!("cargo:rustc-env={}=/dev/null", self.env_name);
76            }
77        };
78
79        Ok(())
80    }
81}
82
83/// Nice error log printer.
84///
85/// ```no_run
86/// use std::process::exit;
87/// use ptx_builder::prelude::*;
88/// # use ptx_builder::error::Result;
89///
90/// fn main() {
91///     if let Err(error) = build() {
92///         eprintln!("{}", ErrorLogPrinter::print(error));
93///         exit(1);
94///    }
95/// }
96/// # fn build() -> Result<()> {
97/// #    use ptx_builder::error::*;
98/// #    Err(BuildErrorKind::InternalError("any...".into()).into())
99/// # }
100pub struct ErrorLogPrinter {
101    error: Box<dyn Fail>,
102    colors: bool,
103}
104
105impl ErrorLogPrinter {
106    /// Creates instance of the printer.
107    pub fn print(error: Error) -> Self {
108        Self {
109            error: Box::new(error),
110            colors: true,
111        }
112    }
113
114    /// Controls whether colors should be used in the error log.
115    pub fn disable_colors(&mut self) -> &mut Self {
116        self.colors = false;
117        self
118    }
119}
120
121trait StringExt {
122    fn prefix_each_line<T>(self, prefix: T) -> Self
123    where
124        T: ToString;
125}
126
127impl StringExt for String {
128    fn prefix_each_line<T: ToString>(self, prefix: T) -> Self {
129        let owned_prefix = prefix.to_string();
130        let glue = String::from("\n") + &owned_prefix;
131
132        owned_prefix + &self.split('\n').collect::<Vec<_>>().join(&glue)
133    }
134}
135
136impl fmt::Display for ErrorLogPrinter {
137    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138        control::set_override(self.colors);
139
140        write!(
141            f,
142            "{}",
143            self.error
144                .to_string()
145                .prefix_each_line("[PTX] ".bright_black())
146        )?;
147
148        for next in self.error.iter_causes() {
149            write!(
150                f,
151                "\n{}",
152                String::from("\n caused by:").prefix_each_line("[PTX]".bright_black())
153            )?;
154
155            write!(
156                f,
157                "\n{}",
158                next.to_string().prefix_each_line("[PTX]   ".bright_black())
159            )?;
160        }
161
162        control::unset_override();
163        Ok(())
164    }
165}