@@ -87,6 +87,28 @@ def _get_staging_dir(*parts: str) -> pathlib.Path:
8787 return base .joinpath (* APP_NAMESPACE , * parts )
8888
8989
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+
90112####################
91113# qnn sdk download management
92114####################
@@ -385,7 +407,11 @@ def _stage_libcxx(target_dir: pathlib.Path):
385407
386408 if not temp_tar .exists ():
387409 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 } " )
389415
390416 logger .info ("[libcxx] Extracting %s" , temp_tar )
391417 with tarfile .open (temp_tar , "r:xz" ) as tar :
0 commit comments