blob: fb6f5265859ff64dd7ec9d2dc1486b069834745e [file] [log] [blame]
Steve Dower0cd63912018-12-10 18:52:57 -08001"""
2Generates a layout of Python for Windows from a build.
3
4See python make_layout.py --help for usage.
5"""
6
7__author__ = "Steve Dower <steve.dower@python.org>"
8__version__ = "3.8"
9
10import argparse
11import functools
12import os
13import re
14import shutil
15import subprocess
16import sys
17import tempfile
18import zipfile
19
20from pathlib import Path
21
22if __name__ == "__main__":
23 # Started directly, so enable relative imports
24 __path__ = [str(Path(__file__).resolve().parent)]
25
26from .support.appxmanifest import *
27from .support.catalog import *
28from .support.constants import *
29from .support.filesets import *
30from .support.logging import *
31from .support.options import *
32from .support.pip import *
33from .support.props import *
Steve Dower21a92f82019-06-14 08:29:20 -070034from .support.nuspec import *
Steve Dower0cd63912018-12-10 18:52:57 -080035
Petr Viktorinc168b502020-12-08 17:36:53 +010036TEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_35", "_ctypes_test", "_test*")
Steve Dower0cd63912018-12-10 18:52:57 -080037TEST_DIRS_ONLY = FileNameSet("test", "tests")
38
39IDLE_DIRS_ONLY = FileNameSet("idlelib")
40
41TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter")
42TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo")
43TCLTK_FILES_ONLY = FileNameSet("turtle.py")
44
45VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip")
46
Steve Dower59c2aa22018-12-27 12:44:25 -080047EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext", "vcruntime*")
Steve Dower0cd63912018-12-10 18:52:57 -080048EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle")
49EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt")
50EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*")
51EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll")
52
Paul Monson32119e12019-03-29 16:30:10 -070053REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*", "libffi*")
Steve Dower0cd63912018-12-10 18:52:57 -080054
55LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt")
56
57PY_FILES = FileSuffixSet(".py")
58PYC_FILES = FileSuffixSet(".pyc")
59CAT_FILES = FileSuffixSet(".cat")
60CDF_FILES = FileSuffixSet(".cdf")
61
62DATA_DIRS = FileNameSet("data")
63
64TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser")
65TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt")
66
Steve Dower21a92f82019-06-14 08:29:20 -070067
Paul Monsonf4e56612019-04-12 09:55:57 -070068def copy_if_modified(src, dest):
69 try:
70 dest_stat = os.stat(dest)
71 except FileNotFoundError:
72 do_copy = True
73 else:
74 src_stat = os.stat(src)
Steve Dower21a92f82019-06-14 08:29:20 -070075 do_copy = (
76 src_stat.st_mtime != dest_stat.st_mtime
77 or src_stat.st_size != dest_stat.st_size
78 )
Paul Monsonf4e56612019-04-12 09:55:57 -070079
80 if do_copy:
81 shutil.copy2(src, dest)
Steve Dower0cd63912018-12-10 18:52:57 -080082
Steve Dower21a92f82019-06-14 08:29:20 -070083
Steve Dower0cd63912018-12-10 18:52:57 -080084def get_lib_layout(ns):
85 def _c(f):
86 if f in EXCLUDE_FROM_LIB:
87 return False
88 if f.is_dir():
89 if f in TEST_DIRS_ONLY:
90 return ns.include_tests
91 if f in TCLTK_DIRS_ONLY:
92 return ns.include_tcltk
93 if f in IDLE_DIRS_ONLY:
94 return ns.include_idle
95 if f in VENV_DIRS_ONLY:
96 return ns.include_venv
97 else:
98 if f in TCLTK_FILES_ONLY:
99 return ns.include_tcltk
Steve Dower0cd63912018-12-10 18:52:57 -0800100 return True
101
102 for dest, src in rglob(ns.source / "Lib", "**/*", _c):
103 yield dest, src
104
Steve Dower0cd63912018-12-10 18:52:57 -0800105
106def get_tcltk_lib(ns):
107 if not ns.include_tcltk:
108 return
109
110 tcl_lib = os.getenv("TCL_LIBRARY")
111 if not tcl_lib or not os.path.isdir(tcl_lib):
112 try:
113 with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f:
114 tcl_lib = f.read().strip()
115 except FileNotFoundError:
116 pass
117 if not tcl_lib or not os.path.isdir(tcl_lib):
Steve Dower21a92f82019-06-14 08:29:20 -0700118 log_warning("Failed to find TCL_LIBRARY")
Steve Dower0cd63912018-12-10 18:52:57 -0800119 return
120
121 for dest, src in rglob(Path(tcl_lib).parent, "**/*"):
122 yield "tcl/{}".format(dest), src
123
124
125def get_layout(ns):
126 def in_build(f, dest="", new_name=None):
127 n, _, x = f.rpartition(".")
128 n = new_name or n
129 src = ns.build / f
130 if ns.debug and src not in REQUIRED_DLLS:
131 if not src.stem.endswith("_d"):
132 src = src.parent / (src.stem + "_d" + src.suffix)
133 if not n.endswith("_d"):
134 n += "_d"
135 f = n + "." + x
136 yield dest + n + "." + x, src
137 if ns.include_symbols:
138 pdb = src.with_suffix(".pdb")
139 if pdb.is_file():
140 yield dest + n + ".pdb", pdb
141 if ns.include_dev:
142 lib = src.with_suffix(".lib")
143 if lib.is_file():
144 yield "libs/" + n + ".lib", lib
145
146 if ns.include_appxmanifest:
Steve Dower1fab9cb2019-08-07 10:49:40 -0700147 yield from in_build("python_uwp.exe", new_name="python{}".format(VER_DOT))
148 yield from in_build("pythonw_uwp.exe", new_name="pythonw{}".format(VER_DOT))
149 # For backwards compatibility, but we don't reference these ourselves.
Steve Dower0cd63912018-12-10 18:52:57 -0800150 yield from in_build("python_uwp.exe", new_name="python")
151 yield from in_build("pythonw_uwp.exe", new_name="pythonw")
152 else:
153 yield from in_build("python.exe", new_name="python")
154 yield from in_build("pythonw.exe", new_name="pythonw")
155
156 yield from in_build(PYTHON_DLL_NAME)
157
158 if ns.include_launchers and ns.include_appxmanifest:
159 if ns.include_pip:
Steve Dower1fab9cb2019-08-07 10:49:40 -0700160 yield from in_build("python_uwp.exe", new_name="pip{}".format(VER_DOT))
Steve Dower0cd63912018-12-10 18:52:57 -0800161 if ns.include_idle:
Steve Dower1fab9cb2019-08-07 10:49:40 -0700162 yield from in_build("pythonw_uwp.exe", new_name="idle{}".format(VER_DOT))
Steve Dower0cd63912018-12-10 18:52:57 -0800163
164 if ns.include_stable:
165 yield from in_build(PYTHON_STABLE_DLL_NAME)
166
Steve Dowerfde44ae2020-03-11 14:12:31 +0000167 found_any = False
Steve Dower0cd63912018-12-10 18:52:57 -0800168 for dest, src in rglob(ns.build, "vcruntime*.dll"):
Steve Dowerfde44ae2020-03-11 14:12:31 +0000169 found_any = True
Steve Dower0cd63912018-12-10 18:52:57 -0800170 yield dest, src
Steve Dowerfde44ae2020-03-11 14:12:31 +0000171 if not found_any:
172 log_error("Failed to locate vcruntime DLL in the build.")
Steve Dower0cd63912018-12-10 18:52:57 -0800173
Steve Dower21a92f82019-06-14 08:29:20 -0700174 yield "LICENSE.txt", ns.build / "LICENSE.txt"
Steve Dower28f6cb32019-01-22 10:49:52 -0800175
Steve Dower0cd63912018-12-10 18:52:57 -0800176 for dest, src in rglob(ns.build, ("*.pyd", "*.dll")):
177 if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS:
178 continue
179 if src in EXCLUDE_FROM_PYDS:
180 continue
181 if src in TEST_PYDS_ONLY and not ns.include_tests:
182 continue
183 if src in TCLTK_PYDS_ONLY and not ns.include_tcltk:
184 continue
185
186 yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/")
187
188 if ns.zip_lib:
189 zip_name = PYTHON_ZIP_NAME
190 yield zip_name, ns.temp / zip_name
191 else:
192 for dest, src in get_lib_layout(ns):
193 yield "Lib/{}".format(dest), src
194
195 if ns.include_venv:
196 yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python")
197 yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw")
198
199 if ns.include_tools:
200
201 def _c(d):
202 if d.is_dir():
203 return d in TOOLS_DIRS
204 return d in TOOLS_FILES
205
206 for dest, src in rglob(ns.source / "Tools", "**/*", _c):
207 yield "Tools/{}".format(dest), src
208
209 if ns.include_underpth:
210 yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME
211
212 if ns.include_dev:
213
Zackery Spytzedb172a2019-10-28 11:03:27 -0600214 for dest, src in rglob(ns.source / "Include", "**/*.h"):
Steve Dower0cd63912018-12-10 18:52:57 -0800215 yield "include/{}".format(dest), src
216 src = ns.source / "PC" / "pyconfig.h"
217 yield "include/pyconfig.h", src
218
219 for dest, src in get_tcltk_lib(ns):
220 yield dest, src
221
222 if ns.include_pip:
Steve Dower21a92f82019-06-14 08:29:20 -0700223 for dest, src in get_pip_layout(ns):
Steve Dower123536f2019-07-24 15:13:22 -0700224 if not isinstance(src, tuple) and (
Steve Dower21a92f82019-06-14 08:29:20 -0700225 src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB
226 ):
227 continue
228 yield dest, src
Steve Dower0cd63912018-12-10 18:52:57 -0800229
230 if ns.include_chm:
231 for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME):
232 yield "Doc/{}".format(dest), src
233
234 if ns.include_html_doc:
235 for dest, src in rglob(ns.doc_build / "html", "**/*"):
236 yield "Doc/html/{}".format(dest), src
237
238 if ns.include_props:
239 for dest, src in get_props_layout(ns):
240 yield dest, src
241
Steve Dower21a92f82019-06-14 08:29:20 -0700242 if ns.include_nuspec:
243 for dest, src in get_nuspec_layout(ns):
244 yield dest, src
245
Steve Dower0cd63912018-12-10 18:52:57 -0800246 for dest, src in get_appx_layout(ns):
247 yield dest, src
248
249 if ns.include_cat:
250 if ns.flat_dlls:
251 yield ns.include_cat.name, ns.include_cat
252 else:
253 yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat
254
255
Steve Dower872bd2b2019-01-08 02:38:01 -0800256def _compile_one_py(src, dest, name, optimize, checked=True):
Steve Dower0cd63912018-12-10 18:52:57 -0800257 import py_compile
258
259 if dest is not None:
260 dest = str(dest)
261
Steve Dower872bd2b2019-01-08 02:38:01 -0800262 mode = (
263 py_compile.PycInvalidationMode.CHECKED_HASH
264 if checked
265 else py_compile.PycInvalidationMode.UNCHECKED_HASH
266 )
267
Steve Dower0cd63912018-12-10 18:52:57 -0800268 try:
269 return Path(
270 py_compile.compile(
271 str(src),
272 dest,
273 str(name),
274 doraise=True,
275 optimize=optimize,
Steve Dower872bd2b2019-01-08 02:38:01 -0800276 invalidation_mode=mode,
Steve Dower0cd63912018-12-10 18:52:57 -0800277 )
278 )
279 except py_compile.PyCompileError:
280 log_warning("Failed to compile {}", src)
281 return None
282
Steve Dowerde148f22019-11-20 09:30:47 -0800283
Bill Collinsc4cda432019-07-25 22:36:58 +0100284# name argument added to address bpo-37641
285def _py_temp_compile(src, name, ns, dest_dir=None, checked=True):
Steve Dower0cd63912018-12-10 18:52:57 -0800286 if not ns.precompile or src not in PY_FILES or src.parent in DATA_DIRS:
287 return None
Bill Collinsc4cda432019-07-25 22:36:58 +0100288 dest = (dest_dir or ns.temp) / (src.stem + ".pyc")
Steve Dowerde148f22019-11-20 09:30:47 -0800289 return _compile_one_py(src, dest, name, optimize=2, checked=checked)
Steve Dower0cd63912018-12-10 18:52:57 -0800290
291
Steve Dower872bd2b2019-01-08 02:38:01 -0800292def _write_to_zip(zf, dest, src, ns, checked=True):
Bill Collinsc4cda432019-07-25 22:36:58 +0100293 pyc = _py_temp_compile(src, dest, ns, checked=checked)
Steve Dower0cd63912018-12-10 18:52:57 -0800294 if pyc:
295 try:
296 zf.write(str(pyc), dest.with_suffix(".pyc"))
297 finally:
298 try:
299 pyc.unlink()
300 except:
301 log_exception("Failed to delete {}", pyc)
302 return
303
304 if src in LIB2TO3_GRAMMAR_FILES:
305 from lib2to3.pgen2.driver import load_grammar
306
307 tmp = ns.temp / src.name
308 try:
309 shutil.copy(src, tmp)
310 load_grammar(str(tmp))
311 for f in ns.temp.glob(src.stem + "*.pickle"):
312 zf.write(str(f), str(dest.parent / f.name))
313 try:
314 f.unlink()
315 except:
316 log_exception("Failed to delete {}", f)
317 except:
318 log_exception("Failed to compile {}", src)
319 finally:
320 try:
321 tmp.unlink()
322 except:
323 log_exception("Failed to delete {}", tmp)
324
325 zf.write(str(src), str(dest))
326
327
328def generate_source_files(ns):
329 if ns.zip_lib:
330 zip_name = PYTHON_ZIP_NAME
331 zip_path = ns.temp / zip_name
332 if zip_path.is_file():
333 zip_path.unlink()
334 elif zip_path.is_dir():
335 log_error(
336 "Cannot create zip file because a directory exists by the same name"
337 )
338 return
339 log_info("Generating {} in {}", zip_name, ns.temp)
340 ns.temp.mkdir(parents=True, exist_ok=True)
341 with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
342 for dest, src in get_lib_layout(ns):
Steve Dower872bd2b2019-01-08 02:38:01 -0800343 _write_to_zip(zf, dest, src, ns, checked=False)
Steve Dower0cd63912018-12-10 18:52:57 -0800344
345 if ns.include_underpth:
346 log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp)
347 ns.temp.mkdir(parents=True, exist_ok=True)
348 with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f:
349 if ns.zip_lib:
350 print(PYTHON_ZIP_NAME, file=f)
351 if ns.include_pip:
352 print("packages", file=f)
353 else:
354 print("Lib", file=f)
355 print("Lib/site-packages", file=f)
356 if not ns.flat_dlls:
357 print("DLLs", file=f)
358 print(".", file=f)
359 print(file=f)
360 print("# Uncomment to run site.main() automatically", file=f)
361 print("#import site", file=f)
362
Steve Dower0cd63912018-12-10 18:52:57 -0800363 if ns.include_pip:
Steve Dower21a92f82019-06-14 08:29:20 -0700364 log_info("Extracting pip")
365 extract_pip_files(ns)
Steve Dower0cd63912018-12-10 18:52:57 -0800366
367
368def _create_zip_file(ns):
369 if not ns.zip:
370 return None
371
372 if ns.zip.is_file():
373 try:
374 ns.zip.unlink()
375 except OSError:
376 log_exception("Unable to remove {}", ns.zip)
377 sys.exit(8)
378 elif ns.zip.is_dir():
379 log_error("Cannot create ZIP file because {} is a directory", ns.zip)
380 sys.exit(8)
381
382 ns.zip.parent.mkdir(parents=True, exist_ok=True)
383 return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED)
384
385
386def copy_files(files, ns):
387 if ns.copy:
388 ns.copy.mkdir(parents=True, exist_ok=True)
389
390 try:
391 total = len(files)
392 except TypeError:
393 total = None
394 count = 0
395
396 zip_file = _create_zip_file(ns)
397 try:
398 need_compile = []
399 in_catalog = []
400
401 for dest, src in files:
402 count += 1
403 if count % 10 == 0:
404 if total:
405 log_info("Processed {:>4} of {} files", count, total)
406 else:
407 log_info("Processed {} files", count)
408 log_debug("Processing {!s}", src)
409
Steve Dower21a92f82019-06-14 08:29:20 -0700410 if isinstance(src, tuple):
411 src, content = src
412 if ns.copy:
413 log_debug("Copy {} -> {}", src, ns.copy / dest)
414 (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True)
415 with open(ns.copy / dest, "wb") as f:
416 f.write(content)
417 if ns.zip:
418 log_debug("Zip {} into {}", src, ns.zip)
419 zip_file.writestr(str(dest), content)
420 continue
421
Steve Dower0cd63912018-12-10 18:52:57 -0800422 if (
423 ns.precompile
424 and src in PY_FILES
425 and src not in EXCLUDE_FROM_COMPILE
426 and src.parent not in DATA_DIRS
427 and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib"))
428 ):
429 if ns.copy:
430 need_compile.append((dest, ns.copy / dest))
431 else:
432 (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True)
Paul Monsonf4e56612019-04-12 09:55:57 -0700433 copy_if_modified(src, ns.temp / "Lib" / dest)
Steve Dower0cd63912018-12-10 18:52:57 -0800434 need_compile.append((dest, ns.temp / "Lib" / dest))
435
436 if src not in EXCLUDE_FROM_CATALOG:
437 in_catalog.append((src.name, src))
438
439 if ns.copy:
440 log_debug("Copy {} -> {}", src, ns.copy / dest)
441 (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True)
442 try:
Paul Monsonf4e56612019-04-12 09:55:57 -0700443 copy_if_modified(src, ns.copy / dest)
Steve Dower0cd63912018-12-10 18:52:57 -0800444 except shutil.SameFileError:
445 pass
446
447 if ns.zip:
448 log_debug("Zip {} into {}", src, ns.zip)
449 zip_file.write(src, str(dest))
450
451 if need_compile:
452 for dest, src in need_compile:
453 compiled = [
454 _compile_one_py(src, None, dest, optimize=0),
455 _compile_one_py(src, None, dest, optimize=1),
456 _compile_one_py(src, None, dest, optimize=2),
457 ]
458 for c in compiled:
459 if not c:
460 continue
461 cdest = Path(dest).parent / Path(c).relative_to(src.parent)
462 if ns.zip:
463 log_debug("Zip {} into {}", c, ns.zip)
464 zip_file.write(c, str(cdest))
465 in_catalog.append((cdest.name, cdest))
466
467 if ns.catalog:
468 # Just write out the CDF now. Compilation and signing is
469 # an extra step
470 log_info("Generating {}", ns.catalog)
471 ns.catalog.parent.mkdir(parents=True, exist_ok=True)
472 write_catalog(ns.catalog, in_catalog)
473
474 finally:
475 if zip_file:
476 zip_file.close()
477
478
479def main():
480 parser = argparse.ArgumentParser()
481 parser.add_argument("-v", help="Increase verbosity", action="count")
482 parser.add_argument(
483 "-s",
484 "--source",
485 metavar="dir",
486 help="The directory containing the repository root",
487 type=Path,
488 default=None,
489 )
490 parser.add_argument(
491 "-b", "--build", metavar="dir", help="Specify the build directory", type=Path
492 )
493 parser.add_argument(
Steve Dowerde148f22019-11-20 09:30:47 -0800494 "--arch",
495 metavar="architecture",
496 help="Specify the target architecture",
497 type=str,
498 default=None,
499 )
500 parser.add_argument(
Steve Dower0cd63912018-12-10 18:52:57 -0800501 "--doc-build",
502 metavar="dir",
503 help="Specify the docs build directory",
504 type=Path,
505 default=None,
506 )
507 parser.add_argument(
508 "--copy",
509 metavar="directory",
510 help="The name of the directory to copy an extracted layout to",
511 type=Path,
512 default=None,
513 )
514 parser.add_argument(
515 "--zip",
516 metavar="file",
517 help="The ZIP file to write all files to",
518 type=Path,
519 default=None,
520 )
521 parser.add_argument(
522 "--catalog",
523 metavar="file",
524 help="The CDF file to write catalog entries to",
525 type=Path,
526 default=None,
527 )
528 parser.add_argument(
529 "--log",
530 metavar="file",
531 help="Write all operations to the specified file",
532 type=Path,
533 default=None,
534 )
535 parser.add_argument(
536 "-t",
537 "--temp",
538 metavar="file",
539 help="A temporary working directory",
540 type=Path,
541 default=None,
542 )
543 parser.add_argument(
544 "-d", "--debug", help="Include debug build", action="store_true"
545 )
546 parser.add_argument(
547 "-p",
548 "--precompile",
549 help="Include .pyc files instead of .py",
550 action="store_true",
551 )
552 parser.add_argument(
553 "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true"
554 )
555 parser.add_argument(
556 "--flat-dlls", help="Does not create a DLLs directory", action="store_true"
557 )
558 parser.add_argument(
559 "-a",
560 "--include-all",
561 help="Include all optional components",
562 action="store_true",
563 )
564 parser.add_argument(
565 "--include-cat",
566 metavar="file",
567 help="Specify the catalog file to include",
568 type=Path,
569 default=None,
570 )
571 for opt, help in get_argparse_options():
572 parser.add_argument(opt, help=help, action="store_true")
573
574 ns = parser.parse_args()
575 update_presets(ns)
576
577 ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent)
578 ns.build = ns.build or Path(sys.executable).parent
579 ns.temp = ns.temp or Path(tempfile.mkdtemp())
580 ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build")
581 if not ns.source.is_absolute():
582 ns.source = (Path.cwd() / ns.source).resolve()
583 if not ns.build.is_absolute():
584 ns.build = (Path.cwd() / ns.build).resolve()
585 if not ns.temp.is_absolute():
586 ns.temp = (Path.cwd() / ns.temp).resolve()
587 if not ns.doc_build.is_absolute():
588 ns.doc_build = (Path.cwd() / ns.doc_build).resolve()
589 if ns.include_cat and not ns.include_cat.is_absolute():
590 ns.include_cat = (Path.cwd() / ns.include_cat).resolve()
Steve Dowerde148f22019-11-20 09:30:47 -0800591 if not ns.arch:
592 ns.arch = "amd64" if sys.maxsize > 2 ** 32 else "win32"
Steve Dower0cd63912018-12-10 18:52:57 -0800593
594 if ns.copy and not ns.copy.is_absolute():
595 ns.copy = (Path.cwd() / ns.copy).resolve()
596 if ns.zip and not ns.zip.is_absolute():
597 ns.zip = (Path.cwd() / ns.zip).resolve()
598 if ns.catalog and not ns.catalog.is_absolute():
599 ns.catalog = (Path.cwd() / ns.catalog).resolve()
600
601 configure_logger(ns)
602
603 log_info(
604 """OPTIONS
605Source: {ns.source}
606Build: {ns.build}
607Temp: {ns.temp}
Steve Dowerde148f22019-11-20 09:30:47 -0800608Arch: {ns.arch}
Steve Dower0cd63912018-12-10 18:52:57 -0800609
610Copy to: {ns.copy}
611Zip to: {ns.zip}
612Catalog: {ns.catalog}""",
613 ns=ns,
614 )
615
Steve Dowerde148f22019-11-20 09:30:47 -0800616 if ns.arch not in ("win32", "amd64", "arm32", "arm64"):
617 log_error("--arch is not a valid value (win32, amd64, arm32, arm64)")
618 return 4
619 if ns.arch in ("arm32", "arm64"):
620 for n in ("include_idle", "include_tcltk"):
621 if getattr(ns, n):
622 log_warning(f"Disabling --{n.replace('_', '-')} on unsupported platform")
623 setattr(ns, n, False)
624
Steve Dower0cd63912018-12-10 18:52:57 -0800625 if ns.include_idle and not ns.include_tcltk:
626 log_warning("Assuming --include-tcltk to support --include-idle")
627 ns.include_tcltk = True
628
629 try:
630 generate_source_files(ns)
631 files = list(get_layout(ns))
632 copy_files(files, ns)
633 except KeyboardInterrupt:
634 log_info("Interrupted by Ctrl+C")
635 return 3
636 except SystemExit:
637 raise
638 except:
639 log_exception("Unhandled error")
640
641 if error_was_logged():
642 log_error("Errors occurred.")
643 return 1
644
645
646if __name__ == "__main__":
647 sys.exit(int(main() or 0))