1
use crate::cache::ZorkCache;
2

            
3
use crate::domain::commands::arguments::Argument;
4
use crate::project_model::ZorkModel;
5
use crate::utils;
6
use crate::utils::constants::{error_messages, COMPILATION_DATABASE};
7
use color_eyre::eyre::{Context, ContextCompat, Result};
8
use serde::Serialize;
9
use std::fs::File;
10
use std::path::{Path, PathBuf};
11

            
12
pub type CompileCommands<'a> = Vec<CompileCommand<'a>>;
13

            
14
/// Generates the `compile_commands.json` file, that acts as a compilation database
15
/// for some static analysis external tools, like `clang-tidy`, and populates it with
16
/// the generated commands for the translation units
17
2
pub(crate) fn map_generated_commands_to_compilation_db(
18
    program_data: &ZorkModel,
19
    cache: &mut ZorkCache,
20
) -> Result<()> {
21
2
    log::debug!("Generating the compilation database...");
22
2
    let compiler = program_data.compiler.cpp_compiler;
23

            
24
2
    let flyweight_data = cache
25
        .generated_commands
26
        .flyweight_data
27
        .as_ref()
28
        .with_context(|| error_messages::FAILURE_LOADING_FLYWEIGHT_DATA)?;
29

            
30
2
    let generated_commands = cache.get_all_commands_iter();
31
    let mut compilation_db_entries: Vec<CompileCommand> =
32
2
        Vec::with_capacity(cache.count_total_generated_commands());
33

            
34
    let compiler_driver: [Argument; 1] =
35
2
        [Argument::from(compiler.get_driver(&program_data.compiler))];
36

            
37
12
    for source_command_line in generated_commands {
38
60
        let translation_unit_cmd_args = compiler_driver
39
            .iter()
40
10
            .chain(flyweight_data.general_args.as_ref().iter())
41
10
            .chain(flyweight_data.shared_args.as_ref().iter())
42
10
            .chain(flyweight_data.std_references.iter())
43
10
            .chain(flyweight_data.compile_but_dont_link.iter())
44
10
            .chain(source_command_line.args.iter())
45
            .collect::<Vec<&Argument>>();
46

            
47
10
        let compile_command = CompileCommand {
48
            directory: &source_command_line.directory,
49
10
            file: &source_command_line.filename,
50
            arguments: translation_unit_cmd_args,
51
        };
52
10
        compilation_db_entries.push(compile_command);
53
    }
54

            
55
2
    let compile_commands_path = Path::new(COMPILATION_DATABASE);
56
2
    if !Path::new(&compile_commands_path).exists() {
57
1
        File::create(compile_commands_path)
58
1
            .with_context(|| "Error creating the compilation database")?;
59
    }
60

            
61
2
    utils::fs::save_file(Path::new(compile_commands_path), &compilation_db_entries)
62
        .with_context(move || "Error saving the compilation database")
63
2
}
64

            
65
/// Data model for serialize the data that will be outputted
66
/// to the `compile_commands.json` compilation database file
67
10
#[derive(Serialize, Debug)]
68
pub struct CompileCommand<'a> {
69
    pub directory: &'a PathBuf,
70
    pub file: &'a String,
71
    pub arguments: Vec<&'a Argument<'a>>,
72
}