88mod frontend {
99 use flate2:: { write:: GzEncoder , Compression } ;
1010 use std:: fs:: File ;
11- use std:: path:: PathBuf ;
11+ use std:: io:: { BufReader , Read } ;
12+ use std:: path:: { Path , PathBuf } ;
1213 use std:: process:: Command ;
1314 use tar:: Builder ;
1415
15- pub fn build_frontend ( ) {
16- println ! ( "cargo:rerun-if-changed=build.rs" ) ;
16+ fn build_pnpm < P : AsRef < Path > > ( out_dir : P ) {
1717 println ! ( "cargo:rerun-if-changed=package.json" ) ;
1818 println ! ( "cargo:rerun-if-changed=../pnpm-lock.yaml" ) ;
1919 println ! ( "cargo:rerun-if-changed=rsbuild.config.ts" ) ;
2020 println ! ( "cargo:rerun-if-changed=frontend" ) ;
21- println ! ( "cargo:rerun-if-changed=dist.tar.gz" ) ;
2221
2322 let status = Command :: new ( "pnpm" )
2423 . arg ( "install" )
@@ -40,17 +39,14 @@ mod frontend {
4039 }
4140
4241 let dist_dir_path = "dist" ;
43- // We put the output in `CARGO_MANIFEST_DIR` instead of `OUT_DIR` because we want to include
44- // it in the crate.
45- let out_dir =
46- PathBuf :: from ( std:: env:: var ( "CARGO_MANIFEST_DIR" ) . expect ( "CARGO_MANIFEST_DIR not set" ) ) ;
47- let output_tar_gz_path = out_dir. join ( "dist.tar.gz" ) ;
42+ let output_tar_gz_path = out_dir. as_ref ( ) . join ( "dist.tar.gz" ) ;
4843
49- if std :: path :: Path :: new ( dist_dir_path) . exists ( ) {
50- let tar_gz_file = File :: create ( & output_tar_gz_path )
51- . expect ( "Failed to create output tar.gz file in CARGO_MANIFEST_DIR " ) ;
44+ if Path :: new ( dist_dir_path) . exists ( ) {
45+ let tar_gz_file =
46+ File :: create ( & output_tar_gz_path ) . expect ( "Failed to create output tar.gz file" ) ;
5247 let enc = GzEncoder :: new ( tar_gz_file, Compression :: default ( ) ) ;
5348 let mut tar_builder = Builder :: new ( enc) ;
49+ tar_builder. mode ( tar:: HeaderMode :: Deterministic ) ;
5450
5551 // Add the entire "dist" directory to the archive, preserving its name within the archive.
5652 tar_builder
@@ -68,9 +64,58 @@ mod frontend {
6864 ) ;
6965 }
7066 }
67+
68+ pub fn build_frontend ( ) {
69+ // We put the output in `CARGO_MANIFEST_DIR` instead of `OUT_DIR` because we want to include
70+ // it in the crate.
71+ build_pnpm ( PathBuf :: from (
72+ std:: env:: var ( "CARGO_MANIFEST_DIR" ) . expect ( "CARGO_MANIFEST_DIR not set" ) ,
73+ ) ) ;
74+ }
75+
76+ pub fn verify_frontend ( ) {
77+ let manifest_dir = std:: env:: var ( "CARGO_MANIFEST_DIR" ) . expect ( "CARGO_MANIFEST_DIR not set" ) ;
78+ let current_dist_path = Path :: new ( & manifest_dir) . join ( "dist.tar.gz" ) ;
79+ let current_dist = File :: open ( & current_dist_path) . expect ( & format ! (
80+ "Failed to open {}" ,
81+ current_dist_path. to_str( ) . unwrap( )
82+ ) ) ;
83+ let current_dist = BufReader :: new ( current_dist) ;
84+
85+ let out_dir = std:: env:: var ( "OUT_DIR" ) . expect ( "OUT_DIR not set" ) ;
86+ build_pnpm ( Path :: new ( & out_dir) ) ;
87+
88+ let new_dist_path = Path :: new ( & out_dir) . join ( "dist.tar.gz" ) ;
89+ let new_dist = File :: open ( & new_dist_path) . expect ( & format ! (
90+ "Failed to open {}" ,
91+ new_dist_path. to_str( ) . unwrap( )
92+ ) ) ;
93+ let new_dist = BufReader :: new ( new_dist) ;
94+
95+ let mut current_bytes = current_dist. bytes ( ) ;
96+ let mut new_bytes = new_dist. bytes ( ) ;
97+ loop {
98+ let a = current_bytes. next ( ) ;
99+ let b = new_bytes. next ( ) ;
100+ if a. is_none ( ) && b. is_none ( ) {
101+ break ;
102+ }
103+ if a. is_none ( ) || b. is_none ( ) || a. unwrap ( ) . unwrap ( ) != b. unwrap ( ) . unwrap ( ) {
104+ println ! ( "cargo::error=Frontend has changed, run `BUILD_FRONTEND=1 cargo build` to update it" ) ;
105+ return ;
106+ }
107+ }
108+ }
71109}
72110
73111fn main ( ) {
74112 #[ cfg( feature = "frontend" ) ]
75- frontend:: build_frontend ( ) ;
113+ {
114+ println ! ( "cargo::rerun-if-env-changed=BUILD_FRONTEND" ) ;
115+ if std:: env:: var ( "BUILD_FRONTEND" ) . is_ok ( ) {
116+ frontend:: build_frontend ( ) ;
117+ } else {
118+ frontend:: verify_frontend ( ) ;
119+ }
120+ }
76121}
0 commit comments