forked from AprilNEA/OpenLogi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.rs
More file actions
212 lines (197 loc) · 8.15 KB
/
Copy pathbuild.rs
File metadata and controls
212 lines (197 loc) · 8.15 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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
//! Build script for openlogi-gui.
//!
//! Besides the existing update-manifest env hook, this embeds the upstream
//! gpui-component themes *without* vendoring copies into this repo. Those theme
//! files live only in the gpui-component git checkout (the compiled crate
//! doesn't ship them), so we ask `cargo metadata` where gpui-component's source
//! actually resides — which is correct across the local git cache, CI, and
//! Nix's vendored `source-git` tree alike — and copy the wanted theme files
//! into `OUT_DIR`, emitting an include list that `theme.rs` pulls in.
//!
//! `OPENLOGI_THEMES_DIR` overrides the lookup with an explicit path to the
//! gpui-component `themes/` directory, as an escape hatch.
//!
//! Uses only `std` plus `embed-resource` on purpose: a build-dependency the
//! resolver hasn't seen would re-resolve the lockfile and bump the
//! precisely-pinned (Cargo.lock-only) gpui rev — so the `cargo metadata` JSON
//! is scanned for `manifest_path` values directly rather than parsed with
//! serde, and `embed-resource` (for [`embed_windows_resources`]) is pinned to
//! the exact version already in the lock as gpui's own build-dependency, which
//! adds an edge, not a crate.
// A build script fails by panicking, so `expect` (with a message that surfaces
// in the build log) is the idiomatic error path here — exempt it from the
// workspace's strict runtime lints.
#![allow(clippy::expect_used)]
use std::fmt::Write as _;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};
/// Upstream theme file stems we embed (everything except our own openlogi.json,
/// which stays a committed, in-repo theme).
const UPSTREAM: &[&str] = &[
"ayu",
"catppuccin",
"tokyonight",
"gruvbox",
"solarized",
"everforest",
"flexoki",
"molokai",
"spaceduck",
"matrix",
"adventure",
"alduin",
"asciinema",
"fahrenheit",
"harper",
"hybrid",
"jellybeans",
"kibble",
"macos-classic",
"mellifluous",
"twilight",
];
fn main() {
println!("cargo:rerun-if-env-changed=OPENLOGI_UPDATE_MANIFEST_URL");
println!("cargo:rerun-if-env-changed=OPENLOGI_THEMES_DIR");
embed_windows_resources();
let out = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
let src_dir = locate_themes_dir();
let dest = out.join("themes");
fs::create_dir_all(&dest).expect("create OUT_DIR/themes");
let mut generated = String::from(
"// @generated by build.rs — upstream gpui-component themes embedded at build time.\n\
static UPSTREAM_THEME_JSON: &[&str] = &[\n",
);
for stem in UPSTREAM {
let src = src_dir.join(format!("{stem}.json"));
let json = fs::read_to_string(&src)
.unwrap_or_else(|e| panic!("cannot read theme `{stem}` at {}: {e}", src.display()));
fs::write(dest.join(format!("{stem}.json")), json).expect("write theme into OUT_DIR");
writeln!(
generated,
" include_str!(concat!(env!(\"OUT_DIR\"), \"/themes/{stem}.json\")),"
)
.expect("writing to a String cannot fail");
println!("cargo:rerun-if-changed={}", src.display());
}
generated.push_str("];\n");
fs::write(out.join("builtin_themes.rs"), generated).expect("write builtin_themes.rs");
}
/// Embed the Windows exe resources — the app icon and a VERSIONINFO block —
/// so Explorer, the taskbar, and Task Manager stop showing the generic blank
/// binary. The `.rc` is generated here rather than committed so the version
/// block tracks `CARGO_PKG_VERSION` (a literal would go stale the moment
/// release-plz bumps). No-op off Windows targets.
///
/// Kept in sync with the twin in `crates/openlogi-agent/build.rs` — only the
/// description/filename strings differ.
fn embed_windows_resources() {
if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
return;
}
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
// rc.exe treats `\` in string literals as escapes; forward slashes are
// accepted by every resource compiler embed-resource can drive.
let icon = manifest_dir
.join("../../design/icon/openlogi.ico")
.display()
.to_string()
.replace('\\', "/");
println!("cargo:rerun-if-changed={icon}");
let (major, minor, patch) = (
env::var("CARGO_PKG_VERSION_MAJOR").expect("CARGO_PKG_VERSION_MAJOR"),
env::var("CARGO_PKG_VERSION_MINOR").expect("CARGO_PKG_VERSION_MINOR"),
env::var("CARGO_PKG_VERSION_PATCH").expect("CARGO_PKG_VERSION_PATCH"),
);
let version = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION");
let rc = format!(
r#"1 ICON "{icon}"
1 VERSIONINFO
FILEVERSION {major},{minor},{patch},0
PRODUCTVERSION {major},{minor},{patch},0
FILEOS 0x40004L
FILETYPE 0x1L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "AprilNEA"
VALUE "FileDescription", "OpenLogi"
VALUE "FileVersion", "{version}"
VALUE "InternalName", "openlogi-gui"
VALUE "OriginalFilename", "OpenLogi.exe"
VALUE "ProductName", "OpenLogi"
VALUE "ProductVersion", "{version}"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
"#
);
let out = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
let rc_path = out.join("openlogi-gui.rc");
fs::write(&rc_path, rc).expect("write generated .rc into OUT_DIR");
// manifest_optional: a missing resource compiler downgrades to a cargo
// warning (icon-less but working exe) instead of failing dev builds on
// machines without the Windows SDK; release builds run on CI runners that
// always carry rc.exe.
embed_resource::compile(&rc_path, embed_resource::NONE)
.manifest_optional()
.expect("compile Windows resources");
}
/// Find the gpui-component `themes/` directory: an explicit override first, else
/// the dependency's real source location as reported by `cargo metadata`.
fn locate_themes_dir() -> PathBuf {
if let Some(dir) = env::var_os("OPENLOGI_THEMES_DIR") {
return PathBuf::from(dir);
}
let metadata = cargo_metadata();
// The themes live at the repo root next to (not inside) the gpui-component
// crate, so walk up from its manifest until a populated `themes/` appears.
// `gpui-component-assets` shares the same checkout root, so either match
// converges on the same directory.
for manifest in manifest_paths(&metadata).filter(|p| p.contains("gpui-component")) {
for ancestor in Path::new(manifest).ancestors() {
let themes = ancestor.join("themes");
if themes.join("catppuccin.json").is_file() {
return themes;
}
}
}
panic!(
"could not locate the gpui-component themes dir via `cargo metadata`; \
set OPENLOGI_THEMES_DIR to the gpui-component `themes/` directory"
);
}
/// Run `cargo metadata` (locked, so it never mutates `Cargo.lock`) and return
/// its JSON stdout.
fn cargo_metadata() -> String {
println!("cargo:rerun-if-changed=../../Cargo.lock");
let cargo = env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
let output = Command::new(cargo)
.args(["metadata", "--format-version=1", "--locked"])
.current_dir(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"))
.output()
.expect("run `cargo metadata`");
assert!(
output.status.success(),
"`cargo metadata` failed: {}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout).expect("`cargo metadata` produced non-UTF-8 output")
}
/// Yield every `manifest_path` string value in the metadata JSON. Paths on the
/// platforms that build the GUI (macOS/Linux) contain no characters that JSON
/// would escape, so a direct scan is enough and avoids a serde build-dep.
fn manifest_paths(json: &str) -> impl Iterator<Item = &str> {
const KEY: &str = "\"manifest_path\":\"";
json.match_indices(KEY).filter_map(|(i, _)| {
let rest = &json[i + KEY.len()..];
rest.find('"').map(|end| &rest[..end])
})
}