Repository navigation
Expand file tree
/
Copy pathbuild.rs
More file actions
114 lines (104 loc) · 3.89 KB
/
Copy pathbuild.rs
File metadata and controls
114 lines (104 loc) · 3.89 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
//! Records which commit a binary was built from, as `NEUTRONSYNC_BUILD_SHA`,
//! so `neutronsync --version` and the About page can name it. Uses only std.
//! The selection order lives in `build/sha.rs` so it can be unit-tested.
use std::path::{Path, PathBuf};
use std::process::Command;
include!("build/sha.rs");
fn main() {
let root = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
println!("cargo:rerun-if-env-changed=NEUTRONSYNC_BUILD_SHA");
println!(
"cargo:rerun-if-changed={}",
root.join(".build-sha").display()
);
if let Some(git_dir) = git_dir(&root) {
for f in ["HEAD", "index"] {
println!("cargo:rerun-if-changed={}", git_dir.join(f).display());
}
// A symbolic HEAD ("ref: refs/heads/x") changes when that ref moves,
// which does not touch HEAD itself.
if let Ok(head) = std::fs::read_to_string(git_dir.join("HEAD")) {
if let Some(r) = head.trim().strip_prefix("ref: ") {
println!(
"cargo:rerun-if-changed={}",
git_dir.join(r.trim()).display()
);
}
}
}
let env_override = std::env::var("NEUTRONSYNC_BUILD_SHA").ok();
let (git_head, git_dirty) = git_state(&root);
// The `-dirty` marker depends on every tracked file, and editing one
// touches neither HEAD nor the index. Watching the tracked files keeps the
// marker honest; cargo only stats them, and git lists them once here.
if git_head.is_some() {
for f in tracked_files(&root) {
println!("cargo:rerun-if-changed={}", root.join(f).display());
}
}
let file = std::fs::read_to_string(root.join(".build-sha")).ok();
let sha = choose_build_sha(
env_override.as_deref(),
git_head.as_deref(),
git_dirty,
file.as_deref(),
);
println!("cargo:rustc-env=NEUTRONSYNC_BUILD_SHA={sha}");
}
/// The repository's `.git` directory, following a worktree's `.git` file.
fn git_dir(root: &Path) -> Option<PathBuf> {
let dot_git = root.join(".git");
if dot_git.is_dir() {
return Some(dot_git);
}
let pointer = std::fs::read_to_string(&dot_git).ok()?;
let target = pointer.trim().strip_prefix("gitdir:")?.trim();
let target = PathBuf::from(target);
Some(if target.is_absolute() {
target
} else {
root.join(target)
})
}
/// Every tracked file, relative to `root`; empty when git is unavailable.
fn tracked_files(root: &Path) -> Vec<String> {
Command::new("git")
.args(["ls-files", "-z"])
.current_dir(root)
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| {
String::from_utf8_lossy(&o.stdout)
.split('\0')
.filter(|f| !f.is_empty())
.map(str::to_string)
.collect()
})
.unwrap_or_default()
}
/// Short hash of HEAD and whether tracked files differ from it. `None` when git
/// is missing, the tree is not a checkout, or git refuses (for example a
/// container checkout owned by another user).
fn git_state(root: &Path) -> (Option<String>, bool) {
let run = |args: &[&str]| -> Option<String> {
let out = Command::new("git")
.args(args)
.current_dir(root)
.output()
.ok()?;
if !out.status.success() {
return None;
}
Some(String::from_utf8_lossy(&out.stdout).trim().to_string())
};
let Some(head) = run(&["rev-parse", "--short=12", "HEAD"]).filter(|h| !h.is_empty()) else {
return (None, false);
};
// Untracked files are not a change to what was built; modified or staged
// tracked files are.
let dirty = run(&["status", "--porcelain", "--untracked-files=no"])
.map(|s| !s.trim().is_empty())
.unwrap_or(false);
(Some(head), dirty)
}