-
Notifications
You must be signed in to change notification settings - Fork 5
/
build.rs
119 lines (102 loc) · 3.77 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use regex::{Captures, RegexBuilder};
use std::{env, fs};
/// Log to cargo's warning output
///
/// Only enabled if environment variable "LOG" is set.
///
/// ```
/// LOG=1 cargo build
/// ```
///
macro_rules! log {
($($tokens: tt)*) => {
env::var("LOG")
.and_then(|_| Ok(print!("cargo:warning={}\n", format!($($tokens)*))))
.unwrap_or_default();
}
}
fn scrape_builtins(paths: String) -> Result<Vec<(String, String)>, ()> {
let mut builtins = vec![];
let paths = std::fs::read_dir(paths).map_err(|_| ())?;
let mut paths = paths.into_iter().filter_map(|i| i.ok()).collect::<Vec<_>>();
paths.sort_by_key(|l| l.file_name());
let re = RegexBuilder::new(r#"#\[builtin\(.*\bsym\s*=\s*\"(.*)\".*\)\].*struct\s+(\w+?)"#)
.multi_line(true)
.dot_matches_new_line(true)
.swap_greed(true)
.crlf(true)
.build()
.expect("Regex is malformed");
for file in paths {
match file.file_type() {
Ok(filetype) if filetype.is_file() => {
let content = fs::read_to_string(file.path()).expect("File not found.");
log!("Scanning file {:?}", file.path().into_os_string());
for (_, [sym, ty]) in re.captures_iter(&content).map(|c| c.extract()) {
log!(" - {ty} as '{sym}'");
builtins.push((String::from(sym), String::from(ty)))
}
}
Ok(filetype) if filetype.is_dir() => {
let dirpath = file.path().into_os_string().into_string().map_err(|_| ())?;
let mut dirbuiltins = scrape_builtins(dirpath)?;
builtins.append(&mut dirbuiltins);
}
_ => continue,
}
}
Ok(builtins)
}
fn update_builtins_table(path: String, builtins: Vec<(String, String)>) -> Result<(), ()> {
let content = fs::read_to_string(&path).expect("Unable to read builtins table file.");
let re = RegexBuilder::new(r"(// builtins start)\s*(\n\s*?).*(\n\s*?// builtins end)")
.multi_line(true)
.dot_matches_new_line(true)
.swap_greed(true)
.crlf(true)
.build()
.expect("Regex is malformed");
let content = re.replace(&content, |cap: &Captures| {
let mut res = String::from("");
let (_, [head, ws, tail]) = cap.extract();
res.push_str(head);
for (sym, ty) in builtins.clone() {
res.push_str(format!(r#"{ws}("{sym}", Box::new({ty}) as Box<dyn Builtin>),"#).as_str());
}
res.push_str(tail);
res
});
log!("Updating {path} ... ");
fs::write(&path, content.to_string()).expect("Error encountered while updating builtins table");
Ok(())
}
fn git_hash() -> String {
use std::process::Command;
let unknown = String::from("unknown");
let changes = !Command::new("git")
.args(["diff", "--cached", "--exit-code"])
.status()
.expect("Error encountered while checking for git changes")
.success();
if changes {
return unknown;
}
Command::new("git")
.arg("rev-parse")
.arg("--verify")
.arg("HEAD")
.output()
.map_or(Ok(unknown), |o| String::from_utf8(o.stdout))
.expect("Error encountered while reading it output")
}
fn main() -> Result<(), ()> {
// embed git hash as environment variable GIT_SHA for use in header
println!("cargo:rustc-env=GIT_HASH={}", git_hash());
// iterate through callable module files and scan for uses of
// `#[builtin(.. sym = "sym" ..)]`
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/callable");
let builtins = scrape_builtins("src/callable".into())?;
update_builtins_table("src/callable/builtins.rs".into(), builtins)?;
Ok(())
}