22import os , sys , argparse , datetime , subprocess , shutil
33from pathlib import Path
44
5- # --- logging, helpers (same as Step 2) ---
5+ # --- Logging ---
66def log_section (logfile , message ):
77 ts = datetime .datetime .now ().strftime ("%Y-%m-%d %H:%M:%S" )
88 section = f"\n { '-' * 40 } \n ({ ts } ) { message } \n { '-' * 40 } \n "
9- print (section ); open (logfile , 'a' , encoding = 'utf-8' ).write (section )
10-
11- # --- Helpers ---
12- def get_target_release (python_target ):
13- mapping = {
14- '311' : ('3119' , '2' ),
15- '312' : ('31210' , '2' ),
16- '313' : ('3135' , '1' ),
17- '314' : ('3140' , '1' )
18- }
19- return mapping .get (python_target , (None , None ))
20-
21- def delete_folder_if_exists (folder : Path ,check_flavor : str = "" ):
9+ print (section )
10+ with open (logfile , 'a' , encoding = 'utf-8' ) as f :
11+ f .write (section )
12+
13+
14+ # --- Utility Functions ---
15+
16+ def delete_folder_if_exists (folder : Path , check_flavor : str = "" ):
2217 check_last = folder .parent .name if not folder .is_dir () else folder .name
23- if folder .exists () and folder .is_dir () and check_last == "bu" + check_flavor :
24- print ("hello" , folder )
25- folder_old = Path (str (folder )+ '.old' )
18+ expected_name = "bu" + check_flavor
19+
20+ if folder .exists () and folder .is_dir () and check_last == expected_name :
21+ print ("Removing old backup:" , folder )
22+ folder_old = folder .with_suffix ('.old' )
2623 if folder_old .exists ():
27- shutil .rmtree (Path (str (folder )+ '.old' ))
28- os .rename (folder , folder_old )
29- shutil .rmtree (Path (str (folder )+ '.old' ))
30- return
31- for item in folder_old :
32- if item .is_dir ():
33- pass
34- # delete_folder_if_exists(item)
35- else :
36- pass
37- # item.unlink()
38- # folder.rmdir()
39-
40- def activate_env (env_path ):
41- # Windows-specific virtual env activation
42- env_script = Path (env_path ) / "scripts" / "env.bat"
43- if not env_script .exists ():
44- raise FileNotFoundError (f"Cannot find env.bat at { env_script } " )
45- # Note: This step is simplified here. Full environment activation logic will be added later.
24+ shutil .rmtree (folder_old )
25+ folder .rename (folder_old )
26+ shutil .rmtree (folder_old )
4627
4728
48- def run_make_py (build_python , winpydirbase , args , logfile ):
49- cmd = [
50- build_python , "-c" ,
51- (
52- "from wppm import make; "
53- f"make.make_all({ args .release } , '{ args .release_level } ', basedir_wpy=r'{ winpydirbase } ', "
54- f"verbose=True, flavor='{ args .flavor } ', source_dirs=r'{ args .source_dirs } ', "
55- f"toolsdirs=r'{ args .tools_dirs } ')" #, portable_dir=r'{args.portable_dir}')"
56- )
57- ]
58- print (cmd )
59- from . import make
60- make .make_all ( args .release , args .release_level , basedir_wpy = winpydirbase ,
61- verbose = True , flavor = args .flavor , source_dirs = args .source_dirs ,
62- toolsdirs = args .tools_dirs ) #, portable_dir= args.portable_dir )
63- #subprocess.run(cmd, stdout=open(logfile, 'a'), stderr=subprocess.STDOUT, check=True)
29+ def run_command (cmd , log_file = None , shell = False , check = True ):
30+ print (f"[RUNNING] { ' ' .join (cmd ) if isinstance (cmd , list ) else cmd } " )
31+ with subprocess .Popen (
32+ cmd , shell = shell , stdout = subprocess .PIPE ,
33+ stderr = subprocess .STDOUT , universal_newlines = True
34+ ) as proc :
35+ with open (log_file , 'a' , encoding = 'utf-8' ) if log_file else open (os .devnull , 'w' ) as logf :
36+ for line in proc .stdout :
37+ print (line , end = "" )
38+ logf .write (line )
39+ if check and proc .wait () != 0 :
40+ raise subprocess .CalledProcessError (proc .returncode , cmd )
41+
6442
6543def pip_install (python_exe : Path , req_file : str , constraints : str , find_links : str , logfile : Path , label : str ):
6644 if req_file and Path (req_file ).exists ():
@@ -69,43 +47,32 @@ def pip_install(python_exe: Path, req_file: str, constraints: str, find_links: s
6947 "-r" , req_file , "-c" , constraints ,
7048 "--pre" , "--no-index" , f"--find-links={ find_links } "
7149 ]
72- log_section (logfile , f"Pip‑ install { label } " )
73- subprocess . run (cmd , stdout = open ( logfile , 'a' ), stderr = subprocess . STDOUT , check = True )
50+ log_section (logfile , f"Pip- install { label } " )
51+ run_command (cmd , log_file = logfile )
7452 else :
7553 log_section (logfile , f"No { label } specified/skipped" )
7654
77- def run_command (cmd , env = None , log_file = None ):
78- print (f"[RUN] { cmd } " )
79- with subprocess .Popen (cmd , shell = True , stdout = subprocess .PIPE , stderr = subprocess .STDOUT , env = env , universal_newlines = True ) as proc :
80- for line in proc .stdout :
81- print (line , end = "" )
82- if log_file :
83- with open (log_file , "a" , encoding = "utf-8" ) as logf :
84- logf .write (line )
85-
86- def patch_winpython (python_exe : Path , logfile : Path ):
87- cmd = [str (python_exe ), "-c" ,
88- (
89- "from wppm import wppm;"
90- "wppm.Distribution().patch_standard_packages('', to_movable=True)"
91- )]
92- print (cmd )
93- subprocess .run (cmd , stdout = open (logfile , 'a' ), stderr = subprocess .STDOUT , check = True )
94- #run_command(f'{activate_env(WINPYDIRBASE)} python -c "from wppm import wppm; wppm.Distribution(r\'{WINPYDIRBASE}\').patch_standard_packages(\'\', to_movable=True)"')
55+
56+ def patch_winpython (python_exe , logfile ):
57+ cmd = [
58+ str (python_exe ), "-c" ,
59+ "from wppm import wppm; wppm.Distribution().patch_standard_packages('', to_movable=True)"
60+ ]
61+ run_command (cmd , log_file = logfile )
9562
9663
9764def check_env_bat (winpydirbase : Path ):
9865 envbat = winpydirbase / "scripts" / "env.bat"
9966 if not envbat .exists ():
10067 raise FileNotFoundError (f"Missing env.bat at { envbat } " )
10168
69+
10270def generate_lockfiles (target_python : Path , winpydirbase : Path , constraints : str , find_links : str , logfile : Path , file_postfix : str ):
10371 pip_req = winpydirbase .parent / "requirement_temp.txt"
10472 with subprocess .Popen ([str (target_python ), "-m" , "pip" , "freeze" ], stdout = subprocess .PIPE ) as proc :
10573 packages = [l for l in proc .stdout if b"winpython" not in l ]
10674 pip_req .write_bytes (b"" .join (packages ))
10775 # Lock to web and local (scaffolding)
108- # if local --no-index --trusted-host=None --find-links="%my_find_links%"
10976 for kind in ("" , "local" ):
11077 out = winpydirbase .parent / f"pylock.{ file_postfix } _{ kind } .toml"
11178 outreq = winpydirbase .parent / f"requir.{ file_postfix } _{ kind } .txt"
@@ -138,22 +105,24 @@ def generate_lockfiles(target_python: Path, winpydirbase: Path, constraints: str
138105 else :
139106 print ("match ok " ,winpydirbase .parent / f"requir.{ file_postfix } _{ web } .txt" , winpydirbase .parent / f"requir.{ file_postfix } _{ local } .txt" )
140107
141- # --- main ---
108+ # --- Main Logic ---
109+ def run_make_py (build_python , winpydirbase , args , logfile ):
110+ from . import make
111+ make .make_all (
112+ args .release , args .release_level , basedir_wpy = winpydirbase ,
113+ verbose = True , flavor = args .flavor ,
114+ source_dirs = args .source_dirs , toolsdirs = args .tools_dirs
115+ )
142116def main ():
143117 parser = argparse .ArgumentParser ()
144-
145118 parser .add_argument ('--python-target' , required = True , help = 'Target Python version, e.g. 311' )
146119 parser .add_argument ('--release' , default = '' , help = 'Release' )
147120 parser .add_argument ('--flavor' , default = '' , help = 'Build flavor' )
148121 parser .add_argument ('--arch' , default = '64' , help = 'Architecture' )
149-
150122 parser .add_argument ('--release-level' , default = 'b1' , help = 'Release level (e.g., b1, rc)' )
151123 parser .add_argument ('--winpydirbase' , required = True , help = 'Path to put environment' )
152124 parser .add_argument ('--source_dirs' , required = True , help = 'Path to directory with python zip' )
153-
154125 parser .add_argument ('--tools_dirs' , required = True , help = 'Path to directory with python zip' )
155- #parser.add_argument('--portable_dir', required=True, help='Path to normal make.py')
156-
157126 parser .add_argument ('--buildenv' , required = True , help = 'Path to build environment' )
158127 parser .add_argument ('--constraints' , default = 'constraints.txt' , help = 'Constraints file' )
159128 parser .add_argument ('--requirements' , help = 'Main requirements.txt file' )
@@ -180,26 +149,20 @@ def main():
180149 #logs termination
181150 z = Path (winpydirbase ).name [(4 + len (args .arch )):- len (args .release_level )]
182151 tada = f"{ z [:1 ]} _{ z [1 :3 ]} _{ z [3 ]} _{ args .release } "
183- winpyver2 = tada .replace ('_' ,'.' )
184- file_postfix = f"{ args .arch } -{ args .python_target [:1 ]} _{ args .python_target [1 :]} _{ args .release } { args .flavor } { args .release_level } "
185- file_postfix = f"{ args .arch } -{ tada } { args .flavor } { args .release_level } "
152+ winpyver2 = tada .replace ('_' , '.' )
153+ file_postfix = f"{ args .arch } -{ tada } { args .flavor } { args .release_level } "
186154
187155 log_section (log_file , f"Preparing build for Python { args .python_target } ({ args .arch } -bit)" )
188156
189157 log_section (log_file , f"🙏 Step 0: displace old { Path (winpydirbase )} " )
190- # O) Pre-clear
191-
192- #delete_folder_if_exists(Path(winpydirbase))
158+
193159 delete_folder_if_exists (winpydirbase .parent , check_flavor = args .flavor ) #bu{flavor]}
194160
195161 log_section (log_file , f"🙏 Step 1: make.py Python with { str (build_python )} at ({ winpydirbase } " )
196- # 1) run make.py
197162 run_make_py (str (build_python ), winpydirbase , args , log_file )
198163
199- # 2) env.bat exists
200164 check_env_bat (winpydirbase )
201165
202- # 3) pip install in built environment
203166 log_section (log_file , "🙏 Step 3: install requirements" )
204167
205168 for label , req in [
@@ -209,11 +172,9 @@ def main():
209172 ]:
210173 pip_install (target_python , req , args .constraints , args .find_links , log_file , label )
211174
212- # 4) patch Winpython
213175 log_section (log_file , "🙏 Step 4: Patch Winpython" )
214176 patch_winpython (target_python , log_file )
215177
216- # 5) Install Wheelhouse
217178 if args .wheelhousereq :
218179 log_section (log_file , f"🙏 Step 5: install wheelhouse requirements { args .wheelhousereq } " )
219180 wheelhousereq = Path (args .wheelhousereq )
@@ -255,15 +216,10 @@ def main():
255216 subprocess .run (cmd , stdout = open (log_file , 'a' ), stderr = subprocess .STDOUT , check = False )
256217
257218
258-
259- # 6) lock files
260219 log_section (log_file , "🙏 Step 6: install lockfiles" )
261220 print (target_python , winpydirbase , args .constraints , args .find_links , log_file )
262- print (' - - -' )
263221 generate_lockfiles (target_python , winpydirbase , args .constraints , args .find_links , log_file , file_postfix )
264222
265- # 5b) Archive lock files
266-
267223 # 6) generate changelog
268224 mdn = f"WinPython{ args .flavor } -{ args .arch } bit-{ winpyver2 } .md"
269225 out = f"WinPython{ args .flavor } -{ args .arch } bit-{ winpyver2 } _History.md"
@@ -286,14 +242,13 @@ def main():
286242 cmd = [str (target_python ), "-c" ,
287243 (
288244 "from wppm import diff;"
289- f"result = diff.compare_package_indexes('{ winpyver2 } ', searchdir=r'{ log_dir } ', flavor=r'{ args .flavor } ', architecture={ args .arch } );"
245+ f"result = diff.compare_package_indexes('{ winpyver2 } ', searchdir=r'{ changelog_dir } ', flavor=r'{ args .flavor } ', architecture={ args .arch } );"
290246 f"open(r'{ winpydirbase .parent / out } ', 'w', encoding='utf-8').write(result)"
291247 )]
292248 subprocess .run (cmd , stdout = open (log_file , 'a' ), stderr = subprocess .STDOUT , check = True )
293249 shutil .copyfile (winpydirbase .parent / out , changelog_dir / out )
294250 log_section (log_file , "✅ Step 6 complete" )
295251
296- # 7) Create Installers
297252 if args .create_installer != "" :
298253 log_section (log_file , "🙏 Step 7 Create Installer" )
299254 stem = f"WinPython{ args .arch } -{ winpyver2 } { args .flavor } { args .release_level } "
0 commit comments