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