1use crate::config::{Profile, Toolchain};
2use crate::extension_manager::Extension;
3use crate::std_command_ext::ExecuteCommand;
4use anyhow::{Context, Result};
5use sha2::{Digest, Sha256};
6use std::fmt::Debug;
7use std::fs;
8use std::hash::Hash;
9use std::path::{Path, PathBuf};
10use std::process::Command;
11
12fn hash<P: AsRef<Path> + Hash + Debug>(path: P) -> Result<String> {
13 let source_file_name = path
14 .as_ref()
15 .file_name()
16 .ok_or_else(|| anyhow::anyhow!("Failed to get file name from {:?}", path))?;
17
18 let content = fs::read_to_string(&path)?;
19 let file = format!("{:?}-{}", path, content);
20
21 let mut hasher = Sha256::new();
22
23 hasher.update(file);
24
25 let hash_bytes = hasher.finalize();
26 let hash: String = hash_bytes
27 .iter()
28 .map(|byte| format!("{:02x}", byte))
29 .collect();
30
31 Ok(format!("{}.{}", source_file_name.to_string_lossy(), hash))
32}
33
34pub struct Compiler {
35 profile: Profile,
36 toolchain: Toolchain,
37 include_path: PathBuf,
38}
39
40impl Compiler {
41 pub fn new(profile: Profile, toolchain: Toolchain, include_path: PathBuf) -> Self {
42 Self {
43 profile,
44 toolchain,
45 include_path,
46 }
47 }
48
49 pub fn compile(&self, source_files: &[PathBuf], output_path: &PathBuf, pic: bool) -> Result<Vec<PathBuf>> {
57 let mut object_files = vec![];
58
59 for source_file in source_files {
60 let output_stem = hash(source_file)
61 .with_context(|| format!("Failed to hash source file {:?}", source_file))?;
62
63 let output_name = Extension::Object.file_name(&output_stem, &self.toolchain.compiler);
64
65 let output_file = output_path.join(output_name);
66
67 object_files.push(output_file.clone());
68
69 let file_exists = fs::exists(&output_file).with_context(|| format!("Failed to check if file exists {:?}", output_file))?;
70 if file_exists {
71 log::info!("Skipping already compiled file {:?}", source_file);
72 continue;
73 }
74 log::info!("Compiling {:?} into {}", source_file, output_path.display());
75
76 let mut command = Command::new(&self.toolchain.compiler);
77
78 if let Some(level) = self.profile.optimization_level.as_gcc_flag() {
79 command.arg(level);
80 }
81
82 command
83 .arg("-I")
84 .arg(&self.include_path);
85
86 command.args(&self.toolchain.compiler_flags);
87
88 if pic {
89 command.arg("-fPIC");
90 }
91
92 command
93 .arg("-c")
94 .arg(source_file);
95
96 command
97 .arg("-o")
98 .arg(&output_file);
99
100 command.execute(true)
101 .with_context(|| format!("Failed to compile file {:?}", source_file))?;
102 }
103
104 Ok(object_files)
105 }
106}