1use std::path::Path;
23
24use rucc_diag::SourceBytes;
25
26pub const DIR: &str = "<builtin>";
32
33const HEADERS: &[(&str, &str)] = &[
39 ("arm_acle.h", include_str!("../runtime/include/arm_acle.h")),
40 ("arm_neon.h", include_str!("../runtime/include/arm_neon.h")),
41 ("avx512bwintrin.h", include_str!("../runtime/include/avx512bwintrin.h")),
42 ("avx512fintrin.h", include_str!("../runtime/include/avx512fintrin.h")),
43 ("avx512vlintrin.h", include_str!("../runtime/include/avx512vlintrin.h")),
44 ("avx512vpopcntdqintrin.h", include_str!("../runtime/include/avx512vpopcntdqintrin.h")),
45 ("cpuid.h", include_str!("../runtime/include/cpuid.h")),
46 ("emmintrin.h", include_str!("../runtime/include/emmintrin.h")),
47 ("float.h", include_str!("../runtime/include/float.h")),
48 ("immintrin.h", include_str!("../runtime/include/immintrin.h")),
49 ("intrin.h", include_str!("../runtime/include/intrin.h")),
50 ("iso646.h", include_str!("../runtime/include/iso646.h")),
51 ("limits.h", include_str!("../runtime/include/limits.h")),
52 ("mm_malloc.h", include_str!("../runtime/include/mm_malloc.h")),
53 ("mmintrin.h", include_str!("../runtime/include/mmintrin.h")),
54 ("nmmintrin.h", include_str!("../runtime/include/nmmintrin.h")),
55 ("pmmintrin.h", include_str!("../runtime/include/pmmintrin.h")),
56 ("popcntintrin.h", include_str!("../runtime/include/popcntintrin.h")),
57 ("setjmp.h", include_str!("../runtime/include/setjmp.h")),
58 ("smmintrin.h", include_str!("../runtime/include/smmintrin.h")),
59 ("stdalign.h", include_str!("../runtime/include/stdalign.h")),
60 ("stdarg.h", include_str!("../runtime/include/stdarg.h")),
61 ("stdatomic.h", include_str!("../runtime/include/stdatomic.h")),
62 ("stdbool.h", include_str!("../runtime/include/stdbool.h")),
63 ("stdckdint.h", include_str!("../runtime/include/stdckdint.h")),
64 ("stddef.h", include_str!("../runtime/include/stddef.h")),
65 ("stdint.h", include_str!("../runtime/include/stdint.h")),
66 ("stdnoreturn.h", include_str!("../runtime/include/stdnoreturn.h")),
67 ("tgmath.h", include_str!("../runtime/include/tgmath.h")),
68 ("tmmintrin.h", include_str!("../runtime/include/tmmintrin.h")),
69 ("vadefs.h", include_str!("../runtime/include/vadefs.h")),
70 ("vpclmulqdqintrin.h", include_str!("../runtime/include/vpclmulqdqintrin.h")),
71 ("x86intrin.h", include_str!("../runtime/include/x86intrin.h")),
72 ("xmmintrin.h", include_str!("../runtime/include/xmmintrin.h")),
73 ("xsaveintrin.h", include_str!("../runtime/include/xsaveintrin.h")),
74];
75
76#[must_use]
78pub fn names() -> Vec<&'static str> {
79 HEADERS.iter().map(|&(name, _)| name).collect()
80}
81
82#[must_use]
84pub fn header(name: &str) -> Option<&'static str> {
85 HEADERS.iter().find(|&&(have, _)| have == name).map(|&(_, text)| text)
86}
87
88#[must_use]
93pub fn read(path: &Path) -> Option<SourceBytes> {
94 if path.parent() != Some(Path::new(DIR)) {
95 return None;
96 }
97 let name = path.file_name()?.to_str()?;
98 header(name).map(SourceBytes::new)
99}
100
101pub fn materialized(cache: &Path) -> std::io::Result<std::path::PathBuf> {
115 let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
116 for &(name, text) in HEADERS {
117 for byte in name.bytes().chain([0]).chain(text.bytes()).chain([0]) {
118 hash = (hash ^ u64::from(byte)).wrapping_mul(0x0100_0000_01b3);
119 }
120 }
121 let dir = cache.join("include").join(format!("{hash:016x}"));
122 if dir.is_dir() {
123 return Ok(dir);
124 }
125 let staging = cache.join("include").join(format!("{hash:016x}.{}", std::process::id()));
126 std::fs::create_dir_all(&staging)?;
127 for &(name, text) in HEADERS {
128 std::fs::write(staging.join(name), text)?;
129 }
130 if let Err(error) = std::fs::rename(&staging, &dir) {
131 let _ = std::fs::remove_dir_all(&staging);
132 if !dir.is_dir() {
133 return Err(error);
134 }
135 }
136 Ok(dir)
137}
138
139#[cfg(test)]
140mod tests {
141 use std::path::PathBuf;
142
143 use super::*;
144
145 #[test]
146 fn the_shipped_headers_are_written_out_whole_and_once() {
147 let cache = std::env::temp_dir().join(format!("rucc-runtime-{}", std::process::id()));
148 let dir = materialized(&cache).expect("the cache is writable");
149 let text = std::fs::read_to_string(dir.join("stdarg.h")).expect("stdarg.h is there");
150 assert_eq!(text, header("stdarg.h").unwrap());
151 assert_eq!(std::fs::read_dir(&dir).unwrap().count(), names().len());
152 assert_eq!(materialized(&cache).unwrap(), dir);
153 assert_eq!(std::fs::read_dir(cache.join("include")).unwrap().count(), 1);
154 std::fs::remove_dir_all(&cache).unwrap();
155 }
156
157 #[test]
158 fn the_shipped_headers_are_read_by_the_name_the_search_path_builds() {
159 let path = PathBuf::from(DIR).join("stdarg.h");
160 let bytes = read(&path).expect("stdarg.h is shipped");
161 let text = String::from_utf8(bytes.as_ref().to_vec()).expect("utf-8");
162 assert!(text.contains("__builtin_va_list"));
163 }
164
165 #[test]
166 fn nothing_outside_the_builtin_directory_is_answered() {
167 assert!(read(Path::new("/usr/include/stdarg.h")).is_none());
168 assert!(read(Path::new("stdarg.h")).is_none());
169 assert!(read(&PathBuf::from(DIR).join("stdio.h")).is_none());
170 assert!(read(&PathBuf::from(DIR).join("sys").join("stdarg.h")).is_none());
171 }
172
173 #[test]
174 fn the_list_is_sorted_so_that_a_new_header_has_one_place_to_go() {
175 let mut sorted = names();
176 sorted.sort_unstable();
177 assert_eq!(names(), sorted);
178 }
179
180 #[test]
184 fn every_header_guards_itself_under_its_own_name() {
185 for &(name, text) in HEADERS {
186 let guard = format!("__RUCC_{}", name.trim_end_matches(".h").to_uppercase());
187 assert!(text.contains(&guard), "{name} does not mention {guard}");
188 }
189 }
190}