corge/tool/
compiler.rs

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    /**
50        @param: source_files - list of source files
51        @param: output_path - output directory path
52        @param: pic - position independent code
53
54        @return: list of compiled object files
55    */
56    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}