patchouly_build/
generate.rs1use 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 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}