feat: 뷰어 타이틀에 빌드 timestamp 표시 — stale 바이너리 판별

사용자 스크린샷에서 타이틀이 'Sprint 4 — Full Bridge / OcctKernel' (구버전
문자열) 으로 표시됨 → 바이너리가 최근 수정(Ortho 카메라·받침 fix 등) 이후
재빌드되지 않은 채 실행 중임을 확인.

앞으로는 타이틀에 'cimery viewer [OcctKernel] — build 2026-04-15 HH:MM:SS'
형식으로 빌드 시각이 박혀서 실행 중 바이너리가 최신인지 즉시 판별 가능.

변경:
- crates/viewer/build.rs 추가:
  · Windows: PowerShell Get-Date, Unix: date 명령어 사용.
  · 실패 시 epoch 초로 폴백.
  · rerun-if-changed=src 로 소스 변경 시 자동 갱신.
- lib.rs: BUILD_TS = env!("BUILD_TIMESTAMP") 상수 도입, 타이틀 포맷 변경.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
minsung
2026-04-15 11:07:33 +09:00
parent 2cb549fd9f
commit 4b8a5db4a7
2 changed files with 52 additions and 5 deletions

View File

@@ -0,0 +1,45 @@
//! 빌드 타임스탬프를 환경변수 BUILD_TIMESTAMP 로 내보냄.
//! `env!("BUILD_TIMESTAMP")` 로 소비. 사용자가 실행 중인 바이너리가 최신 빌드인지
//! 타이틀 바에서 즉시 확인할 수 있게 함.
use std::process::Command;
fn main() {
// 항상 재실행 → 타임스탬프가 매 빌드마다 갱신.
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src");
let ts = current_timestamp();
println!("cargo:rustc-env=BUILD_TIMESTAMP={}", ts);
}
fn current_timestamp() -> String {
// Windows: PowerShell / Unix: date. 실패 시 epoch 초.
#[cfg(target_os = "windows")]
let out = Command::new("powershell")
.args([
"-NoProfile",
"-Command",
"Get-Date -Format 'yyyy-MM-dd HH:mm:ss'",
])
.output();
#[cfg(not(target_os = "windows"))]
let out = Command::new("date").args(["+%Y-%m-%d %H:%M:%S"]).output();
if let Ok(o) = out {
if o.status.success() {
let s = String::from_utf8_lossy(&o.stdout).trim().to_string();
if !s.is_empty() {
return s;
}
}
}
// 폴백: Unix epoch 초
use std::time::{SystemTime, UNIX_EPOCH};
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
format!("epoch-{secs}")
}

View File

@@ -882,14 +882,16 @@ impl Default for CimeryApp {
fn default() -> Self { Self::new() } fn default() -> Self { Self::new() }
} }
/// 빌드 타임스탬프 (build.rs 에서 주입). 타이틀에 표시해서 사용자가 실행 중인
/// 바이너리가 최신 빌드인지 즉시 확인 가능.
const BUILD_TS: &str = env!("BUILD_TIMESTAMP");
impl ApplicationHandler for CimeryApp { impl ApplicationHandler for CimeryApp {
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let kernel = if cfg!(feature = "occt") { "OcctKernel" } else { "PureRustKernel" };
let title = format!("cimery viewer [{kernel}] — build {BUILD_TS}");
let attrs = Window::default_attributes() let attrs = Window::default_attributes()
.with_title(if cfg!(feature = "occt") { .with_title(title)
"cimery viewer [Sprint 4 — Full Bridge / OcctKernel]"
} else {
"cimery viewer [Sprint 4 — Full Bridge / PureRustKernel]"
})
.with_inner_size(winit::dpi::LogicalSize::new(1280u32, 720u32)); .with_inner_size(winit::dpi::LogicalSize::new(1280u32, 720u32));
let window = Arc::new( let window = Arc::new(
event_loop.create_window(attrs).expect("create window"), event_loop.create_window(attrs).expect("create window"),