blue_build/
build_scripts.rs

1use std::{
2    fs::{self, DirEntry, OpenOptions},
3    io::{Read, Write},
4    ops::Not,
5    os::unix::fs::PermissionsExt,
6    path::PathBuf,
7};
8
9use blue_build_utils::constants::{BLUE_BUILD_SCRIPTS_DIR_IGNORE, GITIGNORE_PATH};
10use cached::proc_macro::once;
11use miette::{Context, IntoDiagnostic, Result, miette};
12use rust_embed::Embed;
13
14const SCRIPT_DIR_PREFIX: &str = ".bluebuild-scripts_";
15
16#[derive(Embed)]
17#[folder = "scripts/"]
18pub struct BuildScripts;
19
20impl BuildScripts {
21    /// Extracts the build scripts into the build directory.
22    /// This will also remove any old build scripts that
23    /// were not cleaned up previously.
24    ///
25    /// This operation is performed once per program instance.
26    ///
27    /// # Errors
28    /// Will error if the scripts cannot be extracted or the
29    /// old scripts cannot be deleted.
30    pub fn extract_mount_dir() -> Result<PathBuf> {
31        #[once(result = true)]
32        fn inner() -> Result<PathBuf> {
33            update_gitignore()?;
34            delete_old_dirs()?;
35
36            let dir = PathBuf::from(format!(
37                "{SCRIPT_DIR_PREFIX}{}",
38                crate::shadow::SHORT_COMMIT
39            ));
40            fs::create_dir(&dir)
41                .into_diagnostic()
42                .wrap_err("Failed to create dir for build scripts.")?;
43
44            for file_path in BuildScripts::iter() {
45                let file = BuildScripts::get(file_path.as_ref())
46                    .ok_or_else(|| miette!("Failed to get file {file_path}"))?;
47                let file_path = dir.join(&*file_path);
48                fs::write(&file_path, &file.data)
49                    .into_diagnostic()
50                    .wrap_err_with(|| {
51                        format!("Failed to write build script file {}", file_path.display())
52                    })?;
53
54                let mut perm = fs::metadata(&file_path)
55                    .into_diagnostic()
56                    .wrap_err_with(|| {
57                        format!(
58                            "Failed to get file permissions for file {}",
59                            file_path.display()
60                        )
61                    })?
62                    .permissions();
63
64                perm.set_mode(0o755);
65                fs::set_permissions(&file_path, perm).into_diagnostic()?;
66            }
67
68            Ok(dir)
69        }
70
71        inner()
72    }
73}
74
75fn delete_old_dirs() -> Result<()> {
76    let dirs = fs::read_dir(".")
77        .into_diagnostic()
78        .wrap_err("Failed to read current directory")?
79        .collect::<std::result::Result<Vec<DirEntry>, _>>()
80        .into_diagnostic()
81        .wrap_err("Failed to read dir entry")?;
82
83    for dir in dirs {
84        if dir
85            .file_name()
86            .display()
87            .to_string()
88            .starts_with(SCRIPT_DIR_PREFIX)
89        {
90            fs::remove_dir_all(dir.path())
91                .into_diagnostic()
92                .wrap_err_with(|| {
93                    format!(
94                        "Failed to remove old build script dir at {}",
95                        dir.path().display()
96                    )
97                })?;
98        }
99    }
100
101    Ok(())
102}
103
104fn update_gitignore() -> Result<()> {
105    let file = &mut OpenOptions::new()
106        .read(true)
107        .append(true)
108        .create(true)
109        .open(GITIGNORE_PATH)
110        .into_diagnostic()
111        .wrap_err_with(|| format!("Failed to open {GITIGNORE_PATH} for editing"))?;
112
113    let ignore_contents = {
114        let mut cont = String::new();
115        file.read_to_string(&mut cont)
116            .into_diagnostic()
117            .wrap_err_with(|| format!("Failed to read {GITIGNORE_PATH}"))?;
118        cont
119    };
120
121    if ignore_contents
122        .contains(BLUE_BUILD_SCRIPTS_DIR_IGNORE)
123        .not()
124    {
125        writeln!(file, "{BLUE_BUILD_SCRIPTS_DIR_IGNORE}")
126            .into_diagnostic()
127            .wrap_err_with(|| {
128                format!("Failed to add {BLUE_BUILD_SCRIPTS_DIR_IGNORE} to {GITIGNORE_PATH}")
129            })?;
130    }
131
132    Ok(())
133}