#![doc(html_logo_url = "https://raw.githubusercontent.com/baoyachi/shadow-rs/master/shadow-rs.png")]
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
mod build;
mod ci;
mod env;
mod err;
mod gen_const;
mod git;
mod time;
pub use is_debug::*;
use build::*;
use env::*;
use git::*;
use crate::ci::CiType;
use crate::time::BuildTime;
pub use const_format::*;
use std::collections::HashMap;
use std::env as std_env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use crate::gen_const::{
clap_long_version_branch_const, clap_long_version_tag_const, clap_version_branch_const,
clap_version_tag_const, version_branch_const, version_tag_const, BUILD_CONST_CLAP_LONG_VERSION,
BUILD_CONST_VERSION,
};
use chrono::Local;
pub use err::{SdResult, ShadowError};
pub use git::{branch, git_clean, git_status_file, tag};
trait Format {
fn human_format(&self) -> String;
}
const SHADOW_RS: &str = "shadow.rs";
#[macro_export]
macro_rules! shadow {
($build_mod:ident) => {
pub mod $build_mod {
include!(concat!(env!("OUT_DIR"), "/shadow.rs"));
}
};
}
pub fn new() -> SdResult<()> {
Shadow::build()?;
Ok(())
}
pub fn new_hook<F>(f: F) -> SdResult<()>
where
F: FnOnce(&File) -> SdResult<()>,
{
let shadow = Shadow::build()?;
shadow.hook(f)
}
pub fn get_std_env() -> HashMap<String, String> {
let mut env_map = HashMap::new();
for (k, v) in std_env::vars() {
env_map.insert(k, v);
}
env_map
}
#[derive(Debug)]
pub struct Shadow {
pub f: File,
pub map: HashMap<ShadowConst, ConstVal>,
pub std_env: HashMap<String, String>,
}
impl Shadow {
pub fn hook<F>(&self, f: F) -> SdResult<()>
where
F: FnOnce(&File) -> SdResult<()>,
{
let desc = r#"/// Below code generated by project custom from by build.rs"#;
writeln!(&self.f, "\n{}\n", desc)?;
f(&self.f)?;
Ok(())
}
fn try_ci(&self) -> CiType {
if let Some(c) = self.std_env.get("GITLAB_CI") {
if c == "true" {
return CiType::Gitlab;
}
}
if let Some(c) = self.std_env.get("GITHUB_ACTIONS") {
if c == "true" {
return CiType::Github;
}
}
CiType::None
}
pub fn build() -> SdResult<Shadow> {
let src_path = std::env::var("CARGO_MANIFEST_DIR")?;
let out_path = std::env::var("OUT_DIR")?;
Self::build_inner(src_path, out_path)
}
fn build_inner(src_path: String, out_path: String) -> SdResult<Shadow> {
let out = {
let path = Path::new(out_path.as_str());
if !out_path.ends_with('/') {
path.join(format!("{}/{}", out_path, SHADOW_RS))
} else {
path.join(SHADOW_RS)
}
};
let mut shadow = Shadow {
f: File::create(out)?,
map: Default::default(),
std_env: Default::default(),
};
shadow.std_env = get_std_env();
let ci_type = shadow.try_ci();
let src_path = Path::new(src_path.as_str());
let mut map = new_git(src_path, ci_type, &shadow.std_env);
for (k, v) in new_project(&shadow.std_env) {
map.insert(k, v);
}
for (k, v) in new_system_env(&shadow.std_env) {
map.insert(k, v);
}
shadow.map = map;
shadow.write_all()?;
Ok(shadow)
}
fn write_all(&mut self) -> SdResult<()> {
self.gen_header()?;
self.gen_const()?;
let gen_version = self.gen_version()?;
self.gen_build_in(gen_version)?;
Ok(())
}
pub fn cargo_rerun_if_env_changed(&self) {
for k in self.std_env.keys() {
println!("cargo:rerun-if-env-changed={}", k);
}
}
pub fn cargo_rerun_env_inject(&self, env: &[&str]) {
for k in env {
println!("cargo:rerun-if-env-changed={}", *k);
}
}
fn gen_const(&mut self) -> SdResult<()> {
for (k, v) in self.map.clone() {
println!("cargo:rerun-if-env-changed={}", k);
self.write_const(k, v)?;
}
Ok(())
}
fn gen_header(&self) -> SdResult<()> {
let desc = format!(
r#"// Code generated by shadow-rs generator. DO NOT EDIT.
// Author by:https://www.github.com/baoyachi
// The build script repository:https://github.com/baoyachi/shadow-rs
// Create time by:{}"#,
BuildTime::Local(Local::now()).human_format()
);
writeln!(&self.f, "{}\n\n", desc)?;
Ok(())
}
fn write_const(&mut self, shadow_const: ShadowConst, val: ConstVal) -> SdResult<()> {
let desc = format!("#[doc=r#\"{}\"#]", val.desc);
let define = match val.t {
ConstType::OptStr => format!(
"#[allow(dead_code)]\n\
pub const {} :{} = r#\"{}\"#;",
shadow_const.to_ascii_uppercase(),
ConstType::Str.to_string(),
""
),
ConstType::Str => format!(
"#[allow(dead_code)]\n\
pub const {} :{} = r#\"{}\"#;",
shadow_const.to_ascii_uppercase(),
ConstType::Str.to_string(),
val.v
),
ConstType::Bool => format!(
"#[allow(dead_code)]\n\
pub const {} :{} = {};",
shadow_const.to_ascii_uppercase(),
ConstType::Bool.to_string(),
val.v.parse::<bool>().unwrap()
),
};
writeln!(&self.f, "{}", desc)?;
writeln!(&self.f, "{}\n", define)?;
Ok(())
}
fn gen_version(&mut self) -> SdResult<Vec<&'static str>> {
let (ver_fn, clap_ver_fn, clap_long_ver_fn) = match self.map.get(TAG) {
None => (
version_branch_const(),
clap_version_branch_const(),
clap_long_version_branch_const(),
),
Some(tag) => {
if !tag.v.is_empty() {
(
version_tag_const(),
clap_version_tag_const(),
clap_long_version_tag_const(),
)
} else {
(
version_branch_const(),
clap_version_branch_const(),
clap_long_version_branch_const(),
)
}
}
};
writeln!(&self.f, "{}\n", ver_fn)?;
writeln!(&self.f, "{}\n", clap_ver_fn)?;
writeln!(&self.f, "{}\n", clap_long_ver_fn)?;
Ok(vec![BUILD_CONST_VERSION, BUILD_CONST_CLAP_LONG_VERSION])
}
fn gen_build_in(&self, gen_const: Vec<&'static str>) -> SdResult<()> {
let mut print_val = String::from("\n");
for k in self.map.keys() {
let tmp = format!(r#"{}println!("{}:{{}}", {});{}"#, "\t", k, k, "\n");
print_val.push_str(tmp.as_str());
}
for k in gen_const {
let tmp = format!(r#"{}println!("{}:{{}}\n", {});{}"#, "\t", k, k, "\n");
print_val.push_str(tmp.as_str());
}
let everything_define = format!(
"/// print build in method\n\
#[allow(dead_code)]\n\
pub fn print_build_in() {\
{{}}\
}\n",
print_val,
);
writeln!(&self.f, "{}", everything_define)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn test_build() -> SdResult<()> {
Shadow::build_inner("./".into(), "./".into())?;
let shadow = fs::read_to_string("./shadow.rs")?;
assert!(shadow.len() > 0);
let lines: Vec<_> = shadow.lines().map(|_| true).collect();
assert!(lines.len() > 0);
println!("{}", shadow);
Ok(())
}
#[test]
fn test_env() {
for (k, v) in std::env::vars() {
println!("K:{},V:{}", k, v);
}
}
}