Skip to main content

patchouly_build/
generate.rs

1use std::{error::Error, fs::File, io::Write, path::Path};
2
3use crate::extract::Extraction;
4
5pub fn generate(extraction: Extraction, dest: &Path) -> Result<(), Box<dyn Error>> {
6    let out_bin = dest.join(format!(
7        "{}_stencils.bin",
8        extraction.lib_name.to_lowercase()
9    ));
10    {
11        let mut out_bin = File::options()
12            .create(true)
13            .truncate(true)
14            .write(true)
15            .open(&out_bin)?;
16        out_bin.write_all(&extraction.all_code)?;
17    }
18
19    {
20        let out_rs = dest.join(format!(
21            "{}_stencils.rs",
22            extraction.lib_name.to_lowercase()
23        ));
24        let mut out_rs = File::options()
25            .create(true)
26            .truncate(true)
27            .write(true)
28            .open(&out_rs)?;
29
30        let lib_upper = extraction.lib_name.to_uppercase();
31
32        out_rs.write_all(
33            r#"/// Generated by patchouly-build
34mod stencils {
35
36use patchouly_core::StencilLibrary;
37use patchouly_core::stencils::{Stencil, StencilFamily};
38use patchouly_core::relocation::Relocation;
39"#
40            .as_bytes(),
41        )?;
42
43        out_rs.write_fmt(format_args!(
44            r#"
45pub const {}_STENCIL_LIBRARY: StencilLibrary<{}> = StencilLibrary {{
46    code: include_bytes!({}),
47    empty: b"{}",
48    moves: &{}___MOVE,
49}};"#,
50            lib_upper,
51            extraction.max_regs,
52            escape_string(&out_bin.display().to_string()),
53            extraction
54                .families
55                .get("__empty")
56                .and_then(|empty| {
57                    if empty.stencils.len() != 1 {
58                        return None;
59                    }
60                    let stencil = empty.stencils[0];
61                    Some(escape_binary(stencil.code(&extraction.all_code)))
62                })
63                .unwrap_or("".to_string()),
64            lib_upper,
65        ))?;
66
67        // We directly use from_bits/into_bits here.
68        // I don't think the generated code will run across multiple arches...
69        // TODO: Or do they? Like, when cross-compiling?
70        let endian = cfg!(target_endian = "little");
71        out_rs.write_fmt(format_args!(
72            r#"
73// endianess assertion
74const _: [u8; 1] = [0; (cfg!(target_endian = "little") == {}) as usize];"#,
75            endian
76        ))?;
77
78        for (name, stencil) in extraction.families {
79            if name == "__empty" {
80                continue;
81            }
82
83            out_rs.write_fmt(format_args!(
84                r#"
85pub const {}_{}: StencilFamily<{}, {}, {}, {}, {}> = StencilFamily {{
86    relocation_data: &["#,
87                lib_upper,
88                name.to_uppercase(),
89                stencil.IN,
90                stencil.OUT,
91                stencil.MAX_REGS,
92                stencil.HOLES,
93                stencil.JUMPS,
94            ))?;
95
96            for reloc in &stencil.relocation_data {
97                out_rs.write_fmt(format_args!(
98                    r#"
99        Relocation::from_bits(0x{:x}),"#,
100                    reloc.into_bits()
101                ))?;
102            }
103
104            out_rs.write_all(
105                r#"
106    ],
107    stencils: &["#
108                    .as_bytes(),
109            )?;
110
111            for stencil in &stencil.stencils {
112                out_rs.write_fmt(format_args!(
113                    r#"
114        Stencil::from_bits(0x{:x}),"#,
115                    stencil.into_bits()
116                ))?;
117            }
118
119            out_rs.write_all(
120                r#"
121    ],
122};
123"#
124                .as_bytes(),
125            )?;
126        }
127
128        out_rs.write_all(b"} // mod stencils\n")?;
129    }
130
131    Ok(())
132}
133
134fn escape_string(s: &str) -> String {
135    let hashes = "#".repeat(s.chars().filter(|c| *c == '#').count() + 1);
136    format!("r{}\"{}\"{}", hashes, s, hashes)
137}
138
139fn escape_binary(s: &[u8]) -> String {
140    s.iter().map(|b| format!("\\x{:02x}", b)).collect()
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_escape_string() {
149        assert_eq!(r#####"r##"foo#bar"##"#####, escape_string("foo#bar"));
150    }
151
152    #[test]
153    fn test_escape_binary() {
154        assert_eq!(r#"\x00\x01\x02"#, escape_binary(&[0, 1, 2]));
155    }
156}