genact/modules/
cc.rs

1//! Pretend to run a C compiler
2use std::path::Path;
3
4use async_trait::async_trait;
5use rand::rngs::ThreadRng;
6use rand::seq::IndexedRandom;
7use rand::{Rng, rng};
8
9use crate::args::AppConfig;
10use crate::data::{CFILES_LIST, PACKAGES_LIST};
11use crate::generators::gen_random_n_from_list_into_string;
12use crate::io::{csleep, newline, print};
13use crate::modules::Module;
14
15/// Generate a `String` containing all of the `file_list`'s file's parents as -I flags
16fn generate_includes(file_list: &[&str], max: u32, rng: &mut ThreadRng) -> String {
17    let mut include_flags = vec![];
18    for file in file_list {
19        let path = Path::new(file);
20        if let Some(dir) = path.parent()
21            && let Some(dir_str) = dir.to_str()
22            && !include_flags.contains(&dir_str)
23        {
24            include_flags.push(dir_str);
25        }
26    }
27    let limited_flags = (0..max).map(|_| include_flags.choose(rng).unwrap());
28    limited_flags.fold(String::new(), |acc, x| acc + "-I" + x + " ")
29}
30
31/// Generate a list of `n` random linker flags given a list of `candidates`.
32fn generate_linker_flags(candidates: &[&str], n: usize, rng: &mut ThreadRng) -> String {
33    let libraries = candidates.choose_multiple(rng, n);
34    libraries.fold(String::new(), |acc, &x| acc + "-l" + x + " ")
35}
36
37pub struct Cc;
38
39#[async_trait(?Send)]
40impl Module for Cc {
41    fn name(&self) -> &'static str {
42        "cc"
43    }
44
45    fn signature(&self) -> String {
46        "gcc app.c".to_string()
47    }
48
49    async fn run(&self, appconfig: &AppConfig) {
50        const COMPILERS: &[&str] = &["gcc", "clang"];
51        const FLAGS_OPT: &[&str] = &["-O0", "-O1", "-O2", "-O3", "-Og", "-Os"];
52        const FLAGS_WARN_BASE: &[&str] = &["-Wall", "-Wall -Wextra"];
53        const FLAGS_WARN: &[&str] = &[
54            "-Wno-unused-variable",
55            "-Wno-sign-compare",
56            "-Wno-unknown-pragmas",
57            "-Wno-parentheses",
58            "-Wundef",
59            "-Wwrite-strings",
60            "-Wold-style-definition",
61        ];
62        const FLAGS_F: &[&str] = &["-fsigned-char", "-funroll-loops", "-fgnu89-inline", "-fPIC"];
63        const FLAGS_ARCH: &[&str] = &["-march=x86-64", "-mtune=generic", "-pipe"];
64        const FLAGS_DEF_BASE: &[&str] = &["-DDEBUG", "-DNDEBUG"];
65        const FLAGS_DEF: &[&str] = &[
66            "-D_REENTRANT",
67            "-DMATH_LOOP",
68            "-D_LIBS_REENTRANT",
69            "-DNAMESPACE=lib",
70            "-DMODULE_NAME=lib",
71            "-DPIC",
72            "-DSHARED",
73        ];
74
75        let mut rng = rng();
76
77        // Choose a random package name to be our final linking target.
78        let package = &PACKAGES_LIST.choose(&mut rng).unwrap();
79
80        let compiler = COMPILERS.choose(&mut rng).unwrap();
81
82        let num_cfiles = rng.random_range(100..1000);
83        let mut chosen_files: Vec<&str> = CFILES_LIST
84            .choose_multiple(&mut rng, num_cfiles)
85            .cloned()
86            .collect();
87        chosen_files.sort_unstable();
88
89        let opt = FLAGS_OPT.choose(&mut rng).unwrap();
90
91        // Pick a bunch of warning flags.
92        let warn = FLAGS_WARN_BASE.choose(&mut rng).unwrap().to_string();
93        let num_additional_warn_flags = rng.random_range(0..FLAGS_WARN.len()) as u64;
94        let warn_additional =
95            gen_random_n_from_list_into_string(&mut rng, FLAGS_WARN, num_additional_warn_flags);
96        let warn_final = warn + &warn_additional;
97
98        // Pick a bunch of f flags
99        let num_f_flags = rng.random_range(0..FLAGS_F.len()) as u64;
100        let f = gen_random_n_from_list_into_string(&mut rng, FLAGS_F, num_f_flags);
101
102        // Pick a bunch of architecture flags.
103        let num_arch_flags = rng.random_range(0..FLAGS_ARCH.len()) as u64;
104        let arch = gen_random_n_from_list_into_string(&mut rng, FLAGS_ARCH, num_arch_flags);
105
106        // Get includes for the given files.
107        let includes = generate_includes(chosen_files.as_slice(), 20, &mut rng);
108
109        // Get random linker flags.
110        let num_linker_flags = rng.random_range(0..10);
111        let linker_flags = generate_linker_flags(&PACKAGES_LIST, num_linker_flags, &mut rng);
112
113        // Pick a bunch of defs
114        let defs = FLAGS_DEF_BASE.choose(&mut rng).unwrap().to_string();
115        let num_def_flags = rng.random_range(0..FLAGS_DEF.len()) as u64;
116        let defs_additional =
117            gen_random_n_from_list_into_string(&mut rng, FLAGS_DEF, num_def_flags);
118        let defs_final = defs + &defs_additional;
119
120        // Compile everything.
121        for cfile in &chosen_files {
122            print(format!(
123                "{compiler} -c {opt} {warn}{f}{arch} {includes}{defs} -o {output_file}",
124                compiler = compiler,
125                opt = opt,
126                warn = warn_final,
127                f = f,
128                arch = arch,
129                includes = includes,
130                defs = defs_final,
131                output_file = cfile.replace(".c", ".o")
132            ))
133            .await;
134            newline().await;
135
136            let sleep_length = rng.random_range(30..200);
137            csleep(sleep_length).await;
138
139            if appconfig.should_exit() {
140                return;
141            }
142        }
143
144        // Link everything together.
145        let object_files = chosen_files
146            .iter()
147            .fold(String::new(), |acc, &x| acc + &x.replace(".c", ".o") + " ");
148        print(format!(
149            "{compiler} -o {package} {object_files}{linker_flags}"
150        ))
151        .await;
152        newline().await;
153
154        let sleep_length = rng.random_range(300..1000);
155        csleep(sleep_length).await;
156    }
157}