1- import os
21import pathlib
2+ import json
3+ import hashlib
4+ import os
5+
6+ import colorama
7+ colorama .init ()
8+
9+ def build (source : pathlib .Path , target : pathlib .Path ):
10+ assert (source .suffix in [".zig" , ".rs" , ".c" , ".cpp" , ".cc" ])
11+ assert (target .suffix == ".wasm" )
12+ os .chdir (source .parent )
13+ res = 0
14+ if source .suffix == ".zig" :
15+ res = os .system (f"zig build-exe { source } -target wasm32-freestanding -fno-entry -rdynamic -O ReleaseSmall -femit-bin={ target } " )
16+ elif source .suffix == ".rs" :
17+ return False # Rust builds are currently disabled
18+ res = os .system (f"rustc { source } -target wasm32-unknown-unknown -O -o { target } " )
19+ elif source .suffix in [".cc" , ".c" , ".cpp" ]:
20+ res = os .system (f"emcc { source } -Os -flto -s STANDALONE_WASM -s EXPORTED_FUNCTIONS=\" ['_generateCircuit', '_getUUID', '_getName', '_getDefaultParameters']\" --no-entry -o { target } " )
21+ return res == 0
22+
23+ BuildCache = dict [str , str ]
24+
25+ def load_build_cache (path : pathlib .Path ) -> BuildCache :
26+ if not path .exists ():
27+ return {}
28+ with open (path , "r" ) as f :
29+ return json .load (f )
30+
31+ def get_relative_path (file_path : pathlib .Path , parent : pathlib .Path ) -> str :
32+ try :
33+ return file_path .resolve ().relative_to (parent ).as_posix ()
34+ except ValueError :
35+ return file_path .resolve ().as_posix ()
36+
37+ def normalize_cache (build_cache : BuildCache , script_dir : pathlib .Path ) -> tuple [BuildCache , bool ]:
38+ normalized_cache : BuildCache = {}
39+ cache_normalized = False
40+ for path_str , hash_val in build_cache .items ():
41+ normalized_key = get_relative_path (pathlib .Path (path_str ), script_dir )
42+ if normalized_key != path_str :
43+ cache_normalized = True
44+ normalized_cache [normalized_key ] = hash_val
45+ return normalized_cache , cache_normalized
46+
47+ def get_all_source_files_from_folder (folder_path : pathlib .Path ) -> list [pathlib .Path ]:
48+ for file_group in folder_path .walk ():
49+ for filename in file_group [2 ]:
50+ file = file_group [0 ] / pathlib .Path (filename )
51+ yield file
52+
53+ def get_all_filtered_source_files_from_folder (folder_path : pathlib .Path ) -> list [pathlib .Path ]:
54+ for file in get_all_source_files_from_folder (folder_path ):
55+ if file .suffix not in [".zig" , ".rs" , ".c" , ".cpp" , ".cc" ]:
56+ continue
57+ if file .stem == "cm" :
58+ continue
59+ yield file
60+
61+ def get_all_source_files (folder_paths : list [pathlib .Path ], script_dir : pathlib .Path ) -> list [pathlib .Path ]:
62+ file_paths : list [pathlib .Path ] = []
63+ for folder_path in folder_paths :
64+ for file in get_all_filtered_source_files_from_folder (folder_path ):
65+ file_paths .append (file )
66+ return file_paths
67+
68+ def calculate_file_hash (file_path : pathlib .Path ) -> str :
69+ hasher = hashlib .new ("md5" )
70+ with file_path .open ("r" , encoding = "utf-8" , newline = None ) as f :
71+ while True :
72+ data = f .read (8192 )
73+ if not data :
74+ break
75+ hasher .update (data .encode ("utf-8" ))
76+ return hasher .hexdigest ()
77+
78+ def build_file_considering_cache (file : pathlib .Path , build_cache : BuildCache , target_directory : pathlib .Path , script_dir : pathlib .Path ):
79+ file_hash = calculate_file_hash (file )
80+ file_relative = get_relative_path (file , script_dir )
81+ target_file = target_directory / pathlib .Path (file .stem + '.wasm' )
82+ target_file_relative = get_relative_path (target_file , script_dir )
83+ if file_relative in build_cache :
84+ entry = build_cache [file_relative ]
85+ if entry == file_hash and pathlib .Path (script_dir / target_file_relative ).exists ():
86+ return False , False
87+ print (colorama .Fore .GREEN + "Building" + colorama .Style .RESET_ALL , file )
88+ success = build (file , target_file )
89+ if not success :
90+ print (colorama .Fore .RED + "Build failed for" + colorama .Style .RESET_ALL , file )
91+ return False , True
92+ build_cache [file_relative ] = file_hash
93+ return True , False
94+
95+ def build_files (file_paths : list [pathlib .Path ], build_cache : BuildCache , target_directory : pathlib .Path , script_dir : pathlib .Path ) -> bool :
96+ build_cache_updated = False
97+ error_occured = False
98+ for file in file_paths :
99+ built , error = build_file_considering_cache (file , build_cache , target_directory , script_dir )
100+ if error :
101+ error_occured = True
102+ if built :
103+ build_cache_updated = True
104+ return build_cache_updated , error_occured
105+
106+ def main ():
107+ script_dir = pathlib .Path (__file__ ).parent .resolve ()
108+
109+ build_cache_path = script_dir / "build_cache.json"
110+ build_cache : BuildCache = load_build_cache (build_cache_path )
111+
112+ normalized_cache , cache_normalized = normalize_cache (build_cache , script_dir )
113+ build_cache = normalized_cache
114+
115+ folders = [
116+ "cpp" ,
117+ "zig" ,
118+ # "rust",
119+ ]
3120
4- file_path = ( pathlib . Path ( __file__ ). parent / "wasm" ).resolve ()
5- os . chdir ( file_path )
121+ folder_paths = [( script_dir / folder ).resolve () for folder in folders ]
122+ target_directory = ( script_dir / "wasm" ). resolve ( )
6123
7- file = ".." / pathlib .Path ("cpp/box.cpp" )
8- # file = ".." / pathlib.Path("zig/edge_detector.zig")
9- # file = ".." / pathlib.Path("rust/edge_detector.rs")
124+ file_paths = get_all_source_files (folder_paths , script_dir )
125+ build_cache_updated , error_occured = build_files (file_paths , build_cache , target_directory , script_dir )
126+ if build_cache_updated or cache_normalized :
127+ with open (build_cache_path , "w" ) as f :
128+ json .dump (build_cache , f , indent = 4 )
10129
11- if file .suffix == ".zig" :
12- os .system (f"zig build-exe { file } -target wasm32-freestanding -fno-entry -rdynamic -O ReleaseSmall" )
13- elif file .suffix == ".rs" :
14- os .system (f"rustc { file } -target wasm32-unknown-unknown -O -o { file .stem } .wasm" )
15- elif file .suffix in [".cc" , ".c" , ".cpp" ]:
16- os .system (f"emcc { file } -Os -flto -s STANDALONE_WASM -s EXPORTED_FUNCTIONS=\" ['_generateCircuit', '_getUUID', '_getName', '_getDefaultParameters']\" --no-entry -o { file .stem } .wasm" )
17- else :
18- raise Exception ("File must be .zig, .rx, .c, .cpp, or .cc" )
130+ if not build_cache_updated and not error_occured :
131+ print (colorama .Fore .GREEN + "All files are up to date :)" + colorama .Style .RESET_ALL )
19132
20- # os.system("wasm-objdump -x " + str(file_path / pathlib.Path(file.name[:file.name.rfind(".")])) + ".wasm")
133+ if __name__ == "__main__" :
134+ main ()
0 commit comments