-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.rs
More file actions
198 lines (185 loc) · 7.33 KB
/
Copy pathbuild.rs
File metadata and controls
198 lines (185 loc) · 7.33 KB
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
fn main() {
println!("cargo:rerun-if-env-changed=TALK_BUILD_SHA");
println!("cargo:rerun-if-env-changed=GITHUB_SHA");
emit_git_head_watches();
let sha = std::env::var("TALK_BUILD_SHA")
.ok()
.or_else(|| std::env::var("GITHUB_SHA").ok())
.or_else(|| {
let output = std::process::Command::new("git")
.args(["rev-parse", "HEAD"])
.output()
.ok()?;
if output.status.success() {
String::from_utf8(output.stdout).ok()
} else {
None
}
})
.map(|value| value.trim().to_string())
.filter(|value| value.len() >= 7 && value.bytes().all(|byte| byte.is_ascii_hexdigit()));
if let Some(sha) = sha {
println!("cargo:rustc-env=TALK_BUILD_SHA={sha}");
}
emit_compiler_content_stamp();
compile_native_frontend();
}
/// The compiler content identity (CLEAN-05): a hash of every source
/// file whose change can alter the cached parse/resolve/type products
/// (core and stdlib `TypedProgram` + `Module` payloads) or their
/// serialized layout. Cache keys use it instead of the executable's
/// mtime and length, so relinking after an unrelated change (editor,
/// CLI, MIR, VM) no longer invalidates frontend artifacts.
fn emit_compiler_content_stamp() {
use sha2::Digest as _;
const STAMP_DIRS: &[&str] = &[
"src/common",
"src/parsing",
"src/types",
"src/name_resolution",
"src/typed_ast",
"src/desugar",
"src/procedural_macros",
"src/compiling",
];
const STAMP_FILES: &[&str] = &[
"src/macro_expansion.rs",
"bootstrap/frontend.tbc",
"bootstrap/frontend.abi",
];
// The MIR crates feed on TypedProgram; they never change what the
// frontend cache stores, so mir/ stays out of the stamp.
const SKIP_DIRS: &[&str] = &["src/compiling/mir"];
fn collect(dir: &std::path::Path, skip: &[&str], out: &mut Vec<std::path::PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect(&path, skip, out);
} else if path.extension().is_some_and(|ext| ext == "rs")
&& !skip.iter().any(|skip| path.starts_with(skip))
{
out.push(path);
}
}
}
let mut paths: Vec<std::path::PathBuf> = STAMP_FILES
.iter()
.map(std::path::PathBuf::from)
.collect();
for dir in STAMP_DIRS {
collect(std::path::Path::new(dir), SKIP_DIRS, &mut paths);
}
paths.sort();
let mut hasher = sha2::Sha256::new();
for path in &paths {
println!("cargo:rerun-if-changed={}", path.display());
let Ok(content) = std::fs::read(path) else {
continue;
};
let path = path.to_string_lossy().replace('\\', "/");
hasher.update((path.len() as u64).to_le_bytes());
hasher.update(path.as_bytes());
hasher.update((content.len() as u64).to_le_bytes());
hasher.update(content);
}
let stamp = format!("{:x}", hasher.finalize());
let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR");
std::fs::write(
std::path::Path::new(&out_dir).join("compiler_stamp.txt"),
stamp,
)
.expect("write compiler stamp");
}
/// Compile the checked-in native frontend translation unit for the
/// Cargo target and link the resulting object (ADR 0048). The manifest
/// binds the C to the bootstrap fixed point, so a hand-edited or stale
/// `frontend.c` fails the build here rather than misparsing later. A
/// target without a working C toolchain fails explicitly; there is no
/// bytecode fallback for production parsing.
fn compile_native_frontend() {
use sha2::Digest as _;
// wasm32 has no C toolchain story under wasm-pack; it executes the
// verified bootstrap bytecode in the VM instead (ADR 0048 wasm
// carve-out) and skips the native artifact entirely.
if std::env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("wasm32") {
return;
}
println!("cargo:rerun-if-changed=bootstrap/frontend.c");
println!("cargo:rerun-if-changed=bootstrap/frontend.manifest");
let manifest = std::fs::read_to_string("bootstrap/frontend.manifest")
.expect("bootstrap/frontend.manifest is missing; regenerate with `talk bootstrap`");
let recorded = manifest
.lines()
.find_map(|line| line.trim().strip_prefix("c_digest:"))
.map(str::trim)
.expect(
"bootstrap/frontend.manifest records no c_digest; regenerate with `talk bootstrap`",
);
let source = std::fs::read("bootstrap/frontend.c")
.expect("bootstrap/frontend.c is missing; regenerate with `talk bootstrap`");
let actual = format!("{:x}", sha2::Sha256::digest(&source));
assert_eq!(
recorded, actual,
"bootstrap/frontend.c does not match its manifest; regenerate with `talk bootstrap`"
);
// Full optimization regardless of the Cargo profile: parsing speed
// is the point of the native frontend, dev builds included, and the
// object is cached until the checked-in C changes.
cc::Build::new()
.file("bootstrap/frontend.c")
.opt_level(2)
.flag_if_supported("-std=c11")
.try_compile("talk_frontend_native")
.unwrap_or_else(|error| {
panic!(
"failed to compile the native frontend for this target: {error}\n\
building Talk requires a target C compiler (ADR 0048); \
a target that cannot build the native frontend is unsupported"
)
});
}
/// Watch the checked-out commit for changes. In a plain checkout
/// `.git/HEAD` (and the ref it names) are files under `.git`; in a
/// linked worktree `.git` is a pointer file and both live in the real
/// gitdir. Watching a path that does not exist would rerun this
/// script — and relink every binary — on every build, which also
/// invalidates the compiled-stdlib cache keyed on binary identity.
fn emit_git_head_watches() {
let dotgit = std::path::PathBuf::from(".git");
let gitdir = if dotgit.is_file() {
std::fs::read_to_string(&dotgit).ok().and_then(|content| {
content
.trim()
.strip_prefix("gitdir: ")
.map(std::path::PathBuf::from)
})
} else {
Some(dotgit)
};
let Some(gitdir) = gitdir else { return };
let head_path = gitdir.join("HEAD");
println!("cargo:rerun-if-changed={}", head_path.display());
let Ok(head) = std::fs::read_to_string(&head_path) else {
return;
};
let Some(ref_name) = head.trim().strip_prefix("ref: ") else {
return;
};
// A linked worktree's shared refs live in the common gitdir.
let commondir = std::fs::read_to_string(gitdir.join("commondir"))
.ok()
.map(|path| gitdir.join(path.trim()))
.unwrap_or_else(|| gitdir.clone());
let ref_path = commondir.join(ref_name);
if ref_path.exists() {
println!("cargo:rerun-if-changed={}", ref_path.display());
} else if commondir.join("packed-refs").exists() {
println!(
"cargo:rerun-if-changed={}",
commondir.join("packed-refs").display()
);
}
}