|  | 
| 1 | 1 | use std::env; | 
|  | 2 | +use std::error::Error; | 
| 2 | 3 | use std::fs; | 
| 3 | 4 | use std::path::{Path, PathBuf}; | 
| 4 | 5 | 
 | 
| @@ -92,14 +93,25 @@ impl Build { | 
| 92 | 93 | 
 | 
| 93 | 94 |     /// Builds the Lua artifacts for the specified version. | 
| 94 | 95 |     pub fn build(&self, version: Version) -> Artifacts { | 
| 95 |  | -        let target = &self.target.as_ref().expect("TARGET is not set")[..]; | 
| 96 |  | -        let out_dir = self.out_dir.as_ref().expect("OUT_DIR is not set"); | 
|  | 96 | +        match self.try_build(version) { | 
|  | 97 | +            Ok(artifacts) => artifacts, | 
|  | 98 | +            Err(err) => panic!("{err}"), | 
|  | 99 | +        } | 
|  | 100 | +    } | 
|  | 101 | + | 
|  | 102 | +    /// Attempts to build the Lua artifacts for the specified version. | 
|  | 103 | +    /// | 
|  | 104 | +    /// Returns an error if the build fails. | 
|  | 105 | +    pub fn try_build(&self, version: Version) -> Result<Artifacts, Box<dyn Error>> { | 
|  | 106 | +        let target = self.target.as_ref().ok_or("TARGET is not set")?; | 
|  | 107 | +        let out_dir = self.out_dir.as_ref().ok_or("OUT_DIR is not set")?; | 
| 97 | 108 |         let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); | 
| 98 | 109 |         let mut source_dir = manifest_dir.join(version.source_dir()); | 
| 99 | 110 |         let include_dir = out_dir.join("include"); | 
| 100 | 111 | 
 | 
| 101 | 112 |         if !include_dir.exists() { | 
| 102 |  | -            fs::create_dir_all(&include_dir).unwrap(); | 
|  | 113 | +            fs::create_dir_all(&include_dir) | 
|  | 114 | +                .context(|| format!("Cannot create '{}'", include_dir.display()))?; | 
| 103 | 115 |         } | 
| 104 | 116 | 
 | 
| 105 | 117 |         let mut config = cc::Build::new(); | 
| @@ -148,27 +160,33 @@ impl Build { | 
| 148 | 160 | 
 | 
| 149 | 161 |                 let cpp_source_dir = out_dir.join("cpp_source"); | 
| 150 | 162 |                 if cpp_source_dir.exists() { | 
| 151 |  | -                    fs::remove_dir_all(&cpp_source_dir).unwrap(); | 
|  | 163 | +                    fs::remove_dir_all(&cpp_source_dir) | 
|  | 164 | +                        .context(|| format!("Cannot remove '{}'", cpp_source_dir.display()))?; | 
| 152 | 165 |                 } | 
| 153 |  | -                fs::create_dir_all(&cpp_source_dir).unwrap(); | 
|  | 166 | +                fs::create_dir_all(&cpp_source_dir) | 
|  | 167 | +                    .context(|| format!("Cannot create '{}'", cpp_source_dir.display()))?; | 
| 154 | 168 | 
 | 
| 155 |  | -                for file in fs::read_dir(&source_dir).unwrap() { | 
| 156 |  | -                    let file = file.unwrap(); | 
|  | 169 | +                for file in fs::read_dir(&source_dir) | 
|  | 170 | +                    .context(|| format!("Cannot read '{}'", source_dir.display()))? | 
|  | 171 | +                { | 
|  | 172 | +                    let file = file?; | 
| 157 | 173 |                     let filename = file.file_name(); | 
| 158 |  | -                    let filename = filename.to_str().unwrap(); | 
|  | 174 | +                    let filename = &*filename.to_string_lossy(); | 
| 159 | 175 |                     let src_file = source_dir.join(file.file_name()); | 
| 160 | 176 |                     let dst_file = cpp_source_dir.join(file.file_name()); | 
| 161 | 177 | 
 | 
| 162 |  | -                    let mut content = fs::read(src_file).unwrap(); | 
|  | 178 | +                    let mut content = fs::read(&src_file) | 
|  | 179 | +                        .context(|| format!("Cannot read '{}'", src_file.display()))?; | 
| 163 | 180 |                     if ["lauxlib.h", "lua.h", "lualib.h"].contains(&filename) { | 
| 164 | 181 |                         content.splice(0..0, b"extern \"C\" {\n".to_vec()); | 
| 165 | 182 |                         content.extend(b"\n}".to_vec()) | 
| 166 | 183 |                     } | 
| 167 |  | -                    fs::write(dst_file, content).unwrap(); | 
|  | 184 | +                    fs::write(&dst_file, content) | 
|  | 185 | +                        .context(|| format!("Cannot write to '{}'", dst_file.display()))?; | 
| 168 | 186 |                 } | 
| 169 | 187 |                 source_dir = cpp_source_dir | 
| 170 | 188 |             } | 
| 171 |  | -            _ => panic!("don't know how to build Lua for {target}"), | 
|  | 189 | +            _ => Err(format!("don't know how to build Lua for {target}"))?, | 
| 172 | 190 |         } | 
| 173 | 191 | 
 | 
| 174 | 192 |         if let Lua54 = version { | 
| @@ -199,19 +217,22 @@ impl Build { | 
| 199 | 217 |             .include(&source_dir) | 
| 200 | 218 |             .flag("-w") // Suppress all warnings | 
| 201 | 219 |             .flag_if_supported("-fno-common") // Compile common globals like normal definitions | 
| 202 |  | -            .add_files_by_ext(&source_dir, "c") | 
|  | 220 | +            .add_files_by_ext(&source_dir, "c")? | 
| 203 | 221 |             .out_dir(out_dir) | 
| 204 |  | -            .compile(version.lib_name()); | 
|  | 222 | +            .try_compile(version.lib_name())?; | 
| 205 | 223 | 
 | 
| 206 | 224 |         for f in &["lauxlib.h", "lua.h", "luaconf.h", "lualib.h"] { | 
| 207 |  | -            fs::copy(source_dir.join(f), include_dir.join(f)).unwrap(); | 
|  | 225 | +            let from = source_dir.join(f); | 
|  | 226 | +            let to = include_dir.join(f); | 
|  | 227 | +            fs::copy(&from, &to) | 
|  | 228 | +                .context(|| format!("Cannot copy '{}' to '{}'", from.display(), to.display()))?; | 
| 208 | 229 |         } | 
| 209 | 230 | 
 | 
| 210 |  | -        Artifacts { | 
|  | 231 | +        Ok(Artifacts { | 
| 211 | 232 |             include_dir, | 
| 212 | 233 |             lib_dir: out_dir.clone(), | 
| 213 | 234 |             libs: vec![version.lib_name().to_string()], | 
| 214 |  | -        } | 
|  | 235 | +        }) | 
| 215 | 236 |     } | 
| 216 | 237 | } | 
| 217 | 238 | 
 | 
| @@ -263,19 +284,29 @@ impl Artifacts { | 
| 263 | 284 |     } | 
| 264 | 285 | } | 
| 265 | 286 | 
 | 
|  | 287 | +trait ErrorContext<T> { | 
|  | 288 | +    fn context(self, f: impl FnOnce() -> String) -> Result<T, Box<dyn Error>>; | 
|  | 289 | +} | 
|  | 290 | + | 
|  | 291 | +impl<T, E: Error> ErrorContext<T> for Result<T, E> { | 
|  | 292 | +    fn context(self, f: impl FnOnce() -> String) -> Result<T, Box<dyn Error>> { | 
|  | 293 | +        self.map_err(|e| format!("{}: {e}", f()).into()) | 
|  | 294 | +    } | 
|  | 295 | +} | 
|  | 296 | + | 
| 266 | 297 | trait AddFilesByExt { | 
| 267 |  | -    fn add_files_by_ext(&mut self, dir: &Path, ext: &str) -> &mut Self; | 
|  | 298 | +    fn add_files_by_ext(&mut self, dir: &Path, ext: &str) -> Result<&mut Self, Box<dyn Error>>; | 
| 268 | 299 | } | 
| 269 | 300 | 
 | 
| 270 | 301 | impl AddFilesByExt for cc::Build { | 
| 271 |  | -    fn add_files_by_ext(&mut self, dir: &Path, ext: &str) -> &mut Self { | 
|  | 302 | +    fn add_files_by_ext(&mut self, dir: &Path, ext: &str) -> Result<&mut Self, Box<dyn Error>> { | 
| 272 | 303 |         for entry in fs::read_dir(dir) | 
| 273 |  | -            .unwrap() | 
|  | 304 | +            .context(|| format!("Cannot read '{}'", dir.display()))? | 
| 274 | 305 |             .filter_map(|e| e.ok()) | 
| 275 | 306 |             .filter(|e| e.path().extension() == Some(ext.as_ref())) | 
| 276 | 307 |         { | 
| 277 | 308 |             self.file(entry.path()); | 
| 278 | 309 |         } | 
| 279 |  | -        self | 
|  | 310 | +        Ok(self) | 
| 280 | 311 |     } | 
| 281 | 312 | } | 
0 commit comments