@@ -87,6 +87,28 @@ def _get_staging_dir(*parts: str) -> pathlib.Path:
87
87
return base .joinpath (* APP_NAMESPACE , * parts )
88
88
89
89
90
+ def _atomic_download (url : str , dest : pathlib .Path ):
91
+ """
92
+ Download URL into dest atomically:
93
+ - Write to a temp file in the same dir
94
+ - Move into place if successful
95
+ """
96
+ dest .parent .mkdir (parents = True , exist_ok = True )
97
+
98
+ # Temp file in same dir (guarantees atomic rename)
99
+ with tempfile .NamedTemporaryFile (dir = dest .parent , delete = False ) as tmp :
100
+ tmp_path = pathlib .Path (tmp .name )
101
+
102
+ try :
103
+ urllib .request .urlretrieve (url , tmp_path )
104
+ tmp_path .replace (dest ) # atomic rename
105
+ except Exception :
106
+ # Clean up partial file on failure
107
+ if tmp_path .exists ():
108
+ tmp_path .unlink (missing_ok = True )
109
+ raise
110
+
111
+
90
112
####################
91
113
# qnn sdk download management
92
114
####################
@@ -385,7 +407,11 @@ def _stage_libcxx(target_dir: pathlib.Path):
385
407
386
408
if not temp_tar .exists ():
387
409
logger .info ("[libcxx] Downloading %s" , LLVM_URL )
388
- urllib .request .urlretrieve (LLVM_URL , temp_tar )
410
+ _atomic_download (LLVM_URL , temp_tar )
411
+
412
+ # Sanity check before extracting
413
+ if not temp_tar .exists () or temp_tar .stat ().st_size == 0 :
414
+ raise FileNotFoundError (f"[libcxx] Tarball missing or empty: { temp_tar } " )
389
415
390
416
logger .info ("[libcxx] Extracting %s" , temp_tar )
391
417
with tarfile .open (temp_tar , "r:xz" ) as tar :
0 commit comments