17db96d56Sopenharmony_ci"""
27db96d56Sopenharmony_ciGenerates a layout of Python for Windows from a build.
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_ciSee python make_layout.py --help for usage.
57db96d56Sopenharmony_ci"""
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_ci__author__ = "Steve Dower <steve.dower@python.org>"
87db96d56Sopenharmony_ci__version__ = "3.8"
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ciimport argparse
117db96d56Sopenharmony_ciimport functools
127db96d56Sopenharmony_ciimport os
137db96d56Sopenharmony_ciimport re
147db96d56Sopenharmony_ciimport shutil
157db96d56Sopenharmony_ciimport subprocess
167db96d56Sopenharmony_ciimport sys
177db96d56Sopenharmony_ciimport tempfile
187db96d56Sopenharmony_ciimport zipfile
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_cifrom pathlib import Path
217db96d56Sopenharmony_ci
227db96d56Sopenharmony_ciif __name__ == "__main__":
237db96d56Sopenharmony_ci    # Started directly, so enable relative imports
247db96d56Sopenharmony_ci    __path__ = [str(Path(__file__).resolve().parent)]
257db96d56Sopenharmony_ci
267db96d56Sopenharmony_cifrom .support.appxmanifest import *
277db96d56Sopenharmony_cifrom .support.catalog import *
287db96d56Sopenharmony_cifrom .support.constants import *
297db96d56Sopenharmony_cifrom .support.filesets import *
307db96d56Sopenharmony_cifrom .support.logging import *
317db96d56Sopenharmony_cifrom .support.options import *
327db96d56Sopenharmony_cifrom .support.pip import *
337db96d56Sopenharmony_cifrom .support.props import *
347db96d56Sopenharmony_cifrom .support.nuspec import *
357db96d56Sopenharmony_ci
367db96d56Sopenharmony_ciTEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_35", "_ctypes_test", "_test*")
377db96d56Sopenharmony_ciTEST_DIRS_ONLY = FileNameSet("test", "tests")
387db96d56Sopenharmony_ci
397db96d56Sopenharmony_ciIDLE_DIRS_ONLY = FileNameSet("idlelib")
407db96d56Sopenharmony_ci
417db96d56Sopenharmony_ciTCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter")
427db96d56Sopenharmony_ciTCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo")
437db96d56Sopenharmony_ciTCLTK_FILES_ONLY = FileNameSet("turtle.py")
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_ciVENV_DIRS_ONLY = FileNameSet("venv", "ensurepip")
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ciEXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext", "vcruntime*")
487db96d56Sopenharmony_ciEXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle")
497db96d56Sopenharmony_ciEXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt")
507db96d56Sopenharmony_ciEXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*")
517db96d56Sopenharmony_ciEXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll")
527db96d56Sopenharmony_ci
537db96d56Sopenharmony_ciREQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*", "libffi*")
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ciLIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt")
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_ciPY_FILES = FileSuffixSet(".py")
587db96d56Sopenharmony_ciPYC_FILES = FileSuffixSet(".pyc")
597db96d56Sopenharmony_ciCAT_FILES = FileSuffixSet(".cat")
607db96d56Sopenharmony_ciCDF_FILES = FileSuffixSet(".cdf")
617db96d56Sopenharmony_ci
627db96d56Sopenharmony_ciDATA_DIRS = FileNameSet("data")
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_ciTOOLS_DIRS = FileNameSet("scripts", "i18n", "demo", "parser")
657db96d56Sopenharmony_ciTOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt")
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ci
687db96d56Sopenharmony_cidef copy_if_modified(src, dest):
697db96d56Sopenharmony_ci    try:
707db96d56Sopenharmony_ci        dest_stat = os.stat(dest)
717db96d56Sopenharmony_ci    except FileNotFoundError:
727db96d56Sopenharmony_ci        do_copy = True
737db96d56Sopenharmony_ci    else:
747db96d56Sopenharmony_ci        src_stat = os.stat(src)
757db96d56Sopenharmony_ci        do_copy = (
767db96d56Sopenharmony_ci            src_stat.st_mtime != dest_stat.st_mtime
777db96d56Sopenharmony_ci            or src_stat.st_size != dest_stat.st_size
787db96d56Sopenharmony_ci        )
797db96d56Sopenharmony_ci
807db96d56Sopenharmony_ci    if do_copy:
817db96d56Sopenharmony_ci        shutil.copy2(src, dest)
827db96d56Sopenharmony_ci
837db96d56Sopenharmony_ci
847db96d56Sopenharmony_cidef get_lib_layout(ns):
857db96d56Sopenharmony_ci    def _c(f):
867db96d56Sopenharmony_ci        if f in EXCLUDE_FROM_LIB:
877db96d56Sopenharmony_ci            return False
887db96d56Sopenharmony_ci        if f.is_dir():
897db96d56Sopenharmony_ci            if f in TEST_DIRS_ONLY:
907db96d56Sopenharmony_ci                return ns.include_tests
917db96d56Sopenharmony_ci            if f in TCLTK_DIRS_ONLY:
927db96d56Sopenharmony_ci                return ns.include_tcltk
937db96d56Sopenharmony_ci            if f in IDLE_DIRS_ONLY:
947db96d56Sopenharmony_ci                return ns.include_idle
957db96d56Sopenharmony_ci            if f in VENV_DIRS_ONLY:
967db96d56Sopenharmony_ci                return ns.include_venv
977db96d56Sopenharmony_ci        else:
987db96d56Sopenharmony_ci            if f in TCLTK_FILES_ONLY:
997db96d56Sopenharmony_ci                return ns.include_tcltk
1007db96d56Sopenharmony_ci        return True
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_ci    for dest, src in rglob(ns.source / "Lib", "**/*", _c):
1037db96d56Sopenharmony_ci        yield dest, src
1047db96d56Sopenharmony_ci
1057db96d56Sopenharmony_ci
1067db96d56Sopenharmony_cidef get_tcltk_lib(ns):
1077db96d56Sopenharmony_ci    if not ns.include_tcltk:
1087db96d56Sopenharmony_ci        return
1097db96d56Sopenharmony_ci
1107db96d56Sopenharmony_ci    tcl_lib = os.getenv("TCL_LIBRARY")
1117db96d56Sopenharmony_ci    if not tcl_lib or not os.path.isdir(tcl_lib):
1127db96d56Sopenharmony_ci        try:
1137db96d56Sopenharmony_ci            with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f:
1147db96d56Sopenharmony_ci                tcl_lib = f.read().strip()
1157db96d56Sopenharmony_ci        except FileNotFoundError:
1167db96d56Sopenharmony_ci            pass
1177db96d56Sopenharmony_ci        if not tcl_lib or not os.path.isdir(tcl_lib):
1187db96d56Sopenharmony_ci            log_warning("Failed to find TCL_LIBRARY")
1197db96d56Sopenharmony_ci            return
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ci    for dest, src in rglob(Path(tcl_lib).parent, "**/*"):
1227db96d56Sopenharmony_ci        yield "tcl/{}".format(dest), src
1237db96d56Sopenharmony_ci
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_cidef get_layout(ns):
1267db96d56Sopenharmony_ci    def in_build(f, dest="", new_name=None):
1277db96d56Sopenharmony_ci        n, _, x = f.rpartition(".")
1287db96d56Sopenharmony_ci        n = new_name or n
1297db96d56Sopenharmony_ci        src = ns.build / f
1307db96d56Sopenharmony_ci        if ns.debug and src not in REQUIRED_DLLS:
1317db96d56Sopenharmony_ci            if not src.stem.endswith("_d"):
1327db96d56Sopenharmony_ci                src = src.parent / (src.stem + "_d" + src.suffix)
1337db96d56Sopenharmony_ci            if not n.endswith("_d"):
1347db96d56Sopenharmony_ci                n += "_d"
1357db96d56Sopenharmony_ci                f = n + "." + x
1367db96d56Sopenharmony_ci        yield dest + n + "." + x, src
1377db96d56Sopenharmony_ci        if ns.include_symbols:
1387db96d56Sopenharmony_ci            pdb = src.with_suffix(".pdb")
1397db96d56Sopenharmony_ci            if pdb.is_file():
1407db96d56Sopenharmony_ci                yield dest + n + ".pdb", pdb
1417db96d56Sopenharmony_ci        if ns.include_dev:
1427db96d56Sopenharmony_ci            lib = src.with_suffix(".lib")
1437db96d56Sopenharmony_ci            if lib.is_file():
1447db96d56Sopenharmony_ci                yield "libs/" + n + ".lib", lib
1457db96d56Sopenharmony_ci
1467db96d56Sopenharmony_ci    if ns.include_appxmanifest:
1477db96d56Sopenharmony_ci        yield from in_build("python_uwp.exe", new_name="python{}".format(VER_DOT))
1487db96d56Sopenharmony_ci        yield from in_build("pythonw_uwp.exe", new_name="pythonw{}".format(VER_DOT))
1497db96d56Sopenharmony_ci        # For backwards compatibility, but we don't reference these ourselves.
1507db96d56Sopenharmony_ci        yield from in_build("python_uwp.exe", new_name="python")
1517db96d56Sopenharmony_ci        yield from in_build("pythonw_uwp.exe", new_name="pythonw")
1527db96d56Sopenharmony_ci    else:
1537db96d56Sopenharmony_ci        yield from in_build("python.exe", new_name="python")
1547db96d56Sopenharmony_ci        yield from in_build("pythonw.exe", new_name="pythonw")
1557db96d56Sopenharmony_ci
1567db96d56Sopenharmony_ci    yield from in_build(PYTHON_DLL_NAME)
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_ci    if ns.include_launchers and ns.include_appxmanifest:
1597db96d56Sopenharmony_ci        if ns.include_pip:
1607db96d56Sopenharmony_ci            yield from in_build("python_uwp.exe", new_name="pip{}".format(VER_DOT))
1617db96d56Sopenharmony_ci        if ns.include_idle:
1627db96d56Sopenharmony_ci            yield from in_build("pythonw_uwp.exe", new_name="idle{}".format(VER_DOT))
1637db96d56Sopenharmony_ci
1647db96d56Sopenharmony_ci    if ns.include_stable:
1657db96d56Sopenharmony_ci        yield from in_build(PYTHON_STABLE_DLL_NAME)
1667db96d56Sopenharmony_ci
1677db96d56Sopenharmony_ci    found_any = False
1687db96d56Sopenharmony_ci    for dest, src in rglob(ns.build, "vcruntime*.dll"):
1697db96d56Sopenharmony_ci        found_any = True
1707db96d56Sopenharmony_ci        yield dest, src
1717db96d56Sopenharmony_ci    if not found_any:
1727db96d56Sopenharmony_ci        log_error("Failed to locate vcruntime DLL in the build.")
1737db96d56Sopenharmony_ci
1747db96d56Sopenharmony_ci    yield "LICENSE.txt", ns.build / "LICENSE.txt"
1757db96d56Sopenharmony_ci
1767db96d56Sopenharmony_ci    for dest, src in rglob(ns.build, ("*.pyd", "*.dll")):
1777db96d56Sopenharmony_ci        if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS:
1787db96d56Sopenharmony_ci            continue
1797db96d56Sopenharmony_ci        if src in EXCLUDE_FROM_PYDS:
1807db96d56Sopenharmony_ci            continue
1817db96d56Sopenharmony_ci        if src in TEST_PYDS_ONLY and not ns.include_tests:
1827db96d56Sopenharmony_ci            continue
1837db96d56Sopenharmony_ci        if src in TCLTK_PYDS_ONLY and not ns.include_tcltk:
1847db96d56Sopenharmony_ci            continue
1857db96d56Sopenharmony_ci
1867db96d56Sopenharmony_ci        yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/")
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_ci    if ns.zip_lib:
1897db96d56Sopenharmony_ci        zip_name = PYTHON_ZIP_NAME
1907db96d56Sopenharmony_ci        yield zip_name, ns.temp / zip_name
1917db96d56Sopenharmony_ci    else:
1927db96d56Sopenharmony_ci        for dest, src in get_lib_layout(ns):
1937db96d56Sopenharmony_ci            yield "Lib/{}".format(dest), src
1947db96d56Sopenharmony_ci
1957db96d56Sopenharmony_ci        if ns.include_venv:
1967db96d56Sopenharmony_ci            yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python")
1977db96d56Sopenharmony_ci            yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw")
1987db96d56Sopenharmony_ci
1997db96d56Sopenharmony_ci    if ns.include_tools:
2007db96d56Sopenharmony_ci
2017db96d56Sopenharmony_ci        def _c(d):
2027db96d56Sopenharmony_ci            if d.is_dir():
2037db96d56Sopenharmony_ci                return d in TOOLS_DIRS
2047db96d56Sopenharmony_ci            return d in TOOLS_FILES
2057db96d56Sopenharmony_ci
2067db96d56Sopenharmony_ci        for dest, src in rglob(ns.source / "Tools", "**/*", _c):
2077db96d56Sopenharmony_ci            yield "Tools/{}".format(dest), src
2087db96d56Sopenharmony_ci
2097db96d56Sopenharmony_ci    if ns.include_underpth:
2107db96d56Sopenharmony_ci        yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME
2117db96d56Sopenharmony_ci
2127db96d56Sopenharmony_ci    if ns.include_dev:
2137db96d56Sopenharmony_ci
2147db96d56Sopenharmony_ci        for dest, src in rglob(ns.source / "Include", "**/*.h"):
2157db96d56Sopenharmony_ci            yield "include/{}".format(dest), src
2167db96d56Sopenharmony_ci        src = ns.source / "PC" / "pyconfig.h"
2177db96d56Sopenharmony_ci        yield "include/pyconfig.h", src
2187db96d56Sopenharmony_ci
2197db96d56Sopenharmony_ci    for dest, src in get_tcltk_lib(ns):
2207db96d56Sopenharmony_ci        yield dest, src
2217db96d56Sopenharmony_ci
2227db96d56Sopenharmony_ci    if ns.include_pip:
2237db96d56Sopenharmony_ci        for dest, src in get_pip_layout(ns):
2247db96d56Sopenharmony_ci            if not isinstance(src, tuple) and (
2257db96d56Sopenharmony_ci                src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB
2267db96d56Sopenharmony_ci            ):
2277db96d56Sopenharmony_ci                continue
2287db96d56Sopenharmony_ci            yield dest, src
2297db96d56Sopenharmony_ci
2307db96d56Sopenharmony_ci    if ns.include_chm:
2317db96d56Sopenharmony_ci        for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME):
2327db96d56Sopenharmony_ci            yield "Doc/{}".format(dest), src
2337db96d56Sopenharmony_ci
2347db96d56Sopenharmony_ci    if ns.include_html_doc:
2357db96d56Sopenharmony_ci        for dest, src in rglob(ns.doc_build / "html", "**/*"):
2367db96d56Sopenharmony_ci            yield "Doc/html/{}".format(dest), src
2377db96d56Sopenharmony_ci
2387db96d56Sopenharmony_ci    if ns.include_props:
2397db96d56Sopenharmony_ci        for dest, src in get_props_layout(ns):
2407db96d56Sopenharmony_ci            yield dest, src
2417db96d56Sopenharmony_ci
2427db96d56Sopenharmony_ci    if ns.include_nuspec:
2437db96d56Sopenharmony_ci        for dest, src in get_nuspec_layout(ns):
2447db96d56Sopenharmony_ci            yield dest, src
2457db96d56Sopenharmony_ci
2467db96d56Sopenharmony_ci    for dest, src in get_appx_layout(ns):
2477db96d56Sopenharmony_ci        yield dest, src
2487db96d56Sopenharmony_ci
2497db96d56Sopenharmony_ci    if ns.include_cat:
2507db96d56Sopenharmony_ci        if ns.flat_dlls:
2517db96d56Sopenharmony_ci            yield ns.include_cat.name, ns.include_cat
2527db96d56Sopenharmony_ci        else:
2537db96d56Sopenharmony_ci            yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat
2547db96d56Sopenharmony_ci
2557db96d56Sopenharmony_ci
2567db96d56Sopenharmony_cidef _compile_one_py(src, dest, name, optimize, checked=True):
2577db96d56Sopenharmony_ci    import py_compile
2587db96d56Sopenharmony_ci
2597db96d56Sopenharmony_ci    if dest is not None:
2607db96d56Sopenharmony_ci        dest = str(dest)
2617db96d56Sopenharmony_ci
2627db96d56Sopenharmony_ci    mode = (
2637db96d56Sopenharmony_ci        py_compile.PycInvalidationMode.CHECKED_HASH
2647db96d56Sopenharmony_ci        if checked
2657db96d56Sopenharmony_ci        else py_compile.PycInvalidationMode.UNCHECKED_HASH
2667db96d56Sopenharmony_ci    )
2677db96d56Sopenharmony_ci
2687db96d56Sopenharmony_ci    try:
2697db96d56Sopenharmony_ci        return Path(
2707db96d56Sopenharmony_ci            py_compile.compile(
2717db96d56Sopenharmony_ci                str(src),
2727db96d56Sopenharmony_ci                dest,
2737db96d56Sopenharmony_ci                str(name),
2747db96d56Sopenharmony_ci                doraise=True,
2757db96d56Sopenharmony_ci                optimize=optimize,
2767db96d56Sopenharmony_ci                invalidation_mode=mode,
2777db96d56Sopenharmony_ci            )
2787db96d56Sopenharmony_ci        )
2797db96d56Sopenharmony_ci    except py_compile.PyCompileError:
2807db96d56Sopenharmony_ci        log_warning("Failed to compile {}", src)
2817db96d56Sopenharmony_ci        return None
2827db96d56Sopenharmony_ci
2837db96d56Sopenharmony_ci
2847db96d56Sopenharmony_ci# name argument added to address bpo-37641
2857db96d56Sopenharmony_cidef _py_temp_compile(src, name, ns, dest_dir=None, checked=True):
2867db96d56Sopenharmony_ci    if not ns.precompile or src not in PY_FILES or src.parent in DATA_DIRS:
2877db96d56Sopenharmony_ci        return None
2887db96d56Sopenharmony_ci    dest = (dest_dir or ns.temp) / (src.stem + ".pyc")
2897db96d56Sopenharmony_ci    return _compile_one_py(src, dest, name, optimize=2, checked=checked)
2907db96d56Sopenharmony_ci
2917db96d56Sopenharmony_ci
2927db96d56Sopenharmony_cidef _write_to_zip(zf, dest, src, ns, checked=True):
2937db96d56Sopenharmony_ci    pyc = _py_temp_compile(src, dest, ns, checked=checked)
2947db96d56Sopenharmony_ci    if pyc:
2957db96d56Sopenharmony_ci        try:
2967db96d56Sopenharmony_ci            zf.write(str(pyc), dest.with_suffix(".pyc"))
2977db96d56Sopenharmony_ci        finally:
2987db96d56Sopenharmony_ci            try:
2997db96d56Sopenharmony_ci                pyc.unlink()
3007db96d56Sopenharmony_ci            except:
3017db96d56Sopenharmony_ci                log_exception("Failed to delete {}", pyc)
3027db96d56Sopenharmony_ci        return
3037db96d56Sopenharmony_ci
3047db96d56Sopenharmony_ci    if src in LIB2TO3_GRAMMAR_FILES:
3057db96d56Sopenharmony_ci        from lib2to3.pgen2.driver import load_grammar
3067db96d56Sopenharmony_ci
3077db96d56Sopenharmony_ci        tmp = ns.temp / src.name
3087db96d56Sopenharmony_ci        try:
3097db96d56Sopenharmony_ci            shutil.copy(src, tmp)
3107db96d56Sopenharmony_ci            load_grammar(str(tmp))
3117db96d56Sopenharmony_ci            for f in ns.temp.glob(src.stem + "*.pickle"):
3127db96d56Sopenharmony_ci                zf.write(str(f), str(dest.parent / f.name))
3137db96d56Sopenharmony_ci                try:
3147db96d56Sopenharmony_ci                    f.unlink()
3157db96d56Sopenharmony_ci                except:
3167db96d56Sopenharmony_ci                    log_exception("Failed to delete {}", f)
3177db96d56Sopenharmony_ci        except:
3187db96d56Sopenharmony_ci            log_exception("Failed to compile {}", src)
3197db96d56Sopenharmony_ci        finally:
3207db96d56Sopenharmony_ci            try:
3217db96d56Sopenharmony_ci                tmp.unlink()
3227db96d56Sopenharmony_ci            except:
3237db96d56Sopenharmony_ci                log_exception("Failed to delete {}", tmp)
3247db96d56Sopenharmony_ci
3257db96d56Sopenharmony_ci    zf.write(str(src), str(dest))
3267db96d56Sopenharmony_ci
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_cidef generate_source_files(ns):
3297db96d56Sopenharmony_ci    if ns.zip_lib:
3307db96d56Sopenharmony_ci        zip_name = PYTHON_ZIP_NAME
3317db96d56Sopenharmony_ci        zip_path = ns.temp / zip_name
3327db96d56Sopenharmony_ci        if zip_path.is_file():
3337db96d56Sopenharmony_ci            zip_path.unlink()
3347db96d56Sopenharmony_ci        elif zip_path.is_dir():
3357db96d56Sopenharmony_ci            log_error(
3367db96d56Sopenharmony_ci                "Cannot create zip file because a directory exists by the same name"
3377db96d56Sopenharmony_ci            )
3387db96d56Sopenharmony_ci            return
3397db96d56Sopenharmony_ci        log_info("Generating {} in {}", zip_name, ns.temp)
3407db96d56Sopenharmony_ci        ns.temp.mkdir(parents=True, exist_ok=True)
3417db96d56Sopenharmony_ci        with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
3427db96d56Sopenharmony_ci            for dest, src in get_lib_layout(ns):
3437db96d56Sopenharmony_ci                _write_to_zip(zf, dest, src, ns, checked=False)
3447db96d56Sopenharmony_ci
3457db96d56Sopenharmony_ci    if ns.include_underpth:
3467db96d56Sopenharmony_ci        log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp)
3477db96d56Sopenharmony_ci        ns.temp.mkdir(parents=True, exist_ok=True)
3487db96d56Sopenharmony_ci        with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f:
3497db96d56Sopenharmony_ci            if ns.zip_lib:
3507db96d56Sopenharmony_ci                print(PYTHON_ZIP_NAME, file=f)
3517db96d56Sopenharmony_ci                if ns.include_pip:
3527db96d56Sopenharmony_ci                    print("packages", file=f)
3537db96d56Sopenharmony_ci            else:
3547db96d56Sopenharmony_ci                print("Lib", file=f)
3557db96d56Sopenharmony_ci                print("Lib/site-packages", file=f)
3567db96d56Sopenharmony_ci            if not ns.flat_dlls:
3577db96d56Sopenharmony_ci                print("DLLs", file=f)
3587db96d56Sopenharmony_ci            print(".", file=f)
3597db96d56Sopenharmony_ci            print(file=f)
3607db96d56Sopenharmony_ci            print("# Uncomment to run site.main() automatically", file=f)
3617db96d56Sopenharmony_ci            print("#import site", file=f)
3627db96d56Sopenharmony_ci
3637db96d56Sopenharmony_ci    if ns.include_pip:
3647db96d56Sopenharmony_ci        log_info("Extracting pip")
3657db96d56Sopenharmony_ci        extract_pip_files(ns)
3667db96d56Sopenharmony_ci
3677db96d56Sopenharmony_ci
3687db96d56Sopenharmony_cidef _create_zip_file(ns):
3697db96d56Sopenharmony_ci    if not ns.zip:
3707db96d56Sopenharmony_ci        return None
3717db96d56Sopenharmony_ci
3727db96d56Sopenharmony_ci    if ns.zip.is_file():
3737db96d56Sopenharmony_ci        try:
3747db96d56Sopenharmony_ci            ns.zip.unlink()
3757db96d56Sopenharmony_ci        except OSError:
3767db96d56Sopenharmony_ci            log_exception("Unable to remove {}", ns.zip)
3777db96d56Sopenharmony_ci            sys.exit(8)
3787db96d56Sopenharmony_ci    elif ns.zip.is_dir():
3797db96d56Sopenharmony_ci        log_error("Cannot create ZIP file because {} is a directory", ns.zip)
3807db96d56Sopenharmony_ci        sys.exit(8)
3817db96d56Sopenharmony_ci
3827db96d56Sopenharmony_ci    ns.zip.parent.mkdir(parents=True, exist_ok=True)
3837db96d56Sopenharmony_ci    return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED)
3847db96d56Sopenharmony_ci
3857db96d56Sopenharmony_ci
3867db96d56Sopenharmony_cidef copy_files(files, ns):
3877db96d56Sopenharmony_ci    if ns.copy:
3887db96d56Sopenharmony_ci        ns.copy.mkdir(parents=True, exist_ok=True)
3897db96d56Sopenharmony_ci
3907db96d56Sopenharmony_ci    try:
3917db96d56Sopenharmony_ci        total = len(files)
3927db96d56Sopenharmony_ci    except TypeError:
3937db96d56Sopenharmony_ci        total = None
3947db96d56Sopenharmony_ci    count = 0
3957db96d56Sopenharmony_ci
3967db96d56Sopenharmony_ci    zip_file = _create_zip_file(ns)
3977db96d56Sopenharmony_ci    try:
3987db96d56Sopenharmony_ci        need_compile = []
3997db96d56Sopenharmony_ci        in_catalog = []
4007db96d56Sopenharmony_ci
4017db96d56Sopenharmony_ci        for dest, src in files:
4027db96d56Sopenharmony_ci            count += 1
4037db96d56Sopenharmony_ci            if count % 10 == 0:
4047db96d56Sopenharmony_ci                if total:
4057db96d56Sopenharmony_ci                    log_info("Processed {:>4} of {} files", count, total)
4067db96d56Sopenharmony_ci                else:
4077db96d56Sopenharmony_ci                    log_info("Processed {} files", count)
4087db96d56Sopenharmony_ci            log_debug("Processing {!s}", src)
4097db96d56Sopenharmony_ci
4107db96d56Sopenharmony_ci            if isinstance(src, tuple):
4117db96d56Sopenharmony_ci                src, content = src
4127db96d56Sopenharmony_ci                if ns.copy:
4137db96d56Sopenharmony_ci                    log_debug("Copy {} -> {}", src, ns.copy / dest)
4147db96d56Sopenharmony_ci                    (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True)
4157db96d56Sopenharmony_ci                    with open(ns.copy / dest, "wb") as f:
4167db96d56Sopenharmony_ci                        f.write(content)
4177db96d56Sopenharmony_ci                if ns.zip:
4187db96d56Sopenharmony_ci                    log_debug("Zip {} into {}", src, ns.zip)
4197db96d56Sopenharmony_ci                    zip_file.writestr(str(dest), content)
4207db96d56Sopenharmony_ci                continue
4217db96d56Sopenharmony_ci
4227db96d56Sopenharmony_ci            if (
4237db96d56Sopenharmony_ci                ns.precompile
4247db96d56Sopenharmony_ci                and src in PY_FILES
4257db96d56Sopenharmony_ci                and src not in EXCLUDE_FROM_COMPILE
4267db96d56Sopenharmony_ci                and src.parent not in DATA_DIRS
4277db96d56Sopenharmony_ci                and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib"))
4287db96d56Sopenharmony_ci            ):
4297db96d56Sopenharmony_ci                if ns.copy:
4307db96d56Sopenharmony_ci                    need_compile.append((dest, ns.copy / dest))
4317db96d56Sopenharmony_ci                else:
4327db96d56Sopenharmony_ci                    (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True)
4337db96d56Sopenharmony_ci                    copy_if_modified(src, ns.temp / "Lib" / dest)
4347db96d56Sopenharmony_ci                    need_compile.append((dest, ns.temp / "Lib" / dest))
4357db96d56Sopenharmony_ci
4367db96d56Sopenharmony_ci            if src not in EXCLUDE_FROM_CATALOG:
4377db96d56Sopenharmony_ci                in_catalog.append((src.name, src))
4387db96d56Sopenharmony_ci
4397db96d56Sopenharmony_ci            if ns.copy:
4407db96d56Sopenharmony_ci                log_debug("Copy {} -> {}", src, ns.copy / dest)
4417db96d56Sopenharmony_ci                (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True)
4427db96d56Sopenharmony_ci                try:
4437db96d56Sopenharmony_ci                    copy_if_modified(src, ns.copy / dest)
4447db96d56Sopenharmony_ci                except shutil.SameFileError:
4457db96d56Sopenharmony_ci                    pass
4467db96d56Sopenharmony_ci
4477db96d56Sopenharmony_ci            if ns.zip:
4487db96d56Sopenharmony_ci                log_debug("Zip {} into {}", src, ns.zip)
4497db96d56Sopenharmony_ci                zip_file.write(src, str(dest))
4507db96d56Sopenharmony_ci
4517db96d56Sopenharmony_ci        if need_compile:
4527db96d56Sopenharmony_ci            for dest, src in need_compile:
4537db96d56Sopenharmony_ci                compiled = [
4547db96d56Sopenharmony_ci                    _compile_one_py(src, None, dest, optimize=0),
4557db96d56Sopenharmony_ci                    _compile_one_py(src, None, dest, optimize=1),
4567db96d56Sopenharmony_ci                    _compile_one_py(src, None, dest, optimize=2),
4577db96d56Sopenharmony_ci                ]
4587db96d56Sopenharmony_ci                for c in compiled:
4597db96d56Sopenharmony_ci                    if not c:
4607db96d56Sopenharmony_ci                        continue
4617db96d56Sopenharmony_ci                    cdest = Path(dest).parent / Path(c).relative_to(src.parent)
4627db96d56Sopenharmony_ci                    if ns.zip:
4637db96d56Sopenharmony_ci                        log_debug("Zip {} into {}", c, ns.zip)
4647db96d56Sopenharmony_ci                        zip_file.write(c, str(cdest))
4657db96d56Sopenharmony_ci                    in_catalog.append((cdest.name, cdest))
4667db96d56Sopenharmony_ci
4677db96d56Sopenharmony_ci        if ns.catalog:
4687db96d56Sopenharmony_ci            # Just write out the CDF now. Compilation and signing is
4697db96d56Sopenharmony_ci            # an extra step
4707db96d56Sopenharmony_ci            log_info("Generating {}", ns.catalog)
4717db96d56Sopenharmony_ci            ns.catalog.parent.mkdir(parents=True, exist_ok=True)
4727db96d56Sopenharmony_ci            write_catalog(ns.catalog, in_catalog)
4737db96d56Sopenharmony_ci
4747db96d56Sopenharmony_ci    finally:
4757db96d56Sopenharmony_ci        if zip_file:
4767db96d56Sopenharmony_ci            zip_file.close()
4777db96d56Sopenharmony_ci
4787db96d56Sopenharmony_ci
4797db96d56Sopenharmony_cidef main():
4807db96d56Sopenharmony_ci    parser = argparse.ArgumentParser()
4817db96d56Sopenharmony_ci    parser.add_argument("-v", help="Increase verbosity", action="count")
4827db96d56Sopenharmony_ci    parser.add_argument(
4837db96d56Sopenharmony_ci        "-s",
4847db96d56Sopenharmony_ci        "--source",
4857db96d56Sopenharmony_ci        metavar="dir",
4867db96d56Sopenharmony_ci        help="The directory containing the repository root",
4877db96d56Sopenharmony_ci        type=Path,
4887db96d56Sopenharmony_ci        default=None,
4897db96d56Sopenharmony_ci    )
4907db96d56Sopenharmony_ci    parser.add_argument(
4917db96d56Sopenharmony_ci        "-b", "--build", metavar="dir", help="Specify the build directory", type=Path
4927db96d56Sopenharmony_ci    )
4937db96d56Sopenharmony_ci    parser.add_argument(
4947db96d56Sopenharmony_ci        "--arch",
4957db96d56Sopenharmony_ci        metavar="architecture",
4967db96d56Sopenharmony_ci        help="Specify the target architecture",
4977db96d56Sopenharmony_ci        type=str,
4987db96d56Sopenharmony_ci        default=None,
4997db96d56Sopenharmony_ci    )
5007db96d56Sopenharmony_ci    parser.add_argument(
5017db96d56Sopenharmony_ci        "--doc-build",
5027db96d56Sopenharmony_ci        metavar="dir",
5037db96d56Sopenharmony_ci        help="Specify the docs build directory",
5047db96d56Sopenharmony_ci        type=Path,
5057db96d56Sopenharmony_ci        default=None,
5067db96d56Sopenharmony_ci    )
5077db96d56Sopenharmony_ci    parser.add_argument(
5087db96d56Sopenharmony_ci        "--copy",
5097db96d56Sopenharmony_ci        metavar="directory",
5107db96d56Sopenharmony_ci        help="The name of the directory to copy an extracted layout to",
5117db96d56Sopenharmony_ci        type=Path,
5127db96d56Sopenharmony_ci        default=None,
5137db96d56Sopenharmony_ci    )
5147db96d56Sopenharmony_ci    parser.add_argument(
5157db96d56Sopenharmony_ci        "--zip",
5167db96d56Sopenharmony_ci        metavar="file",
5177db96d56Sopenharmony_ci        help="The ZIP file to write all files to",
5187db96d56Sopenharmony_ci        type=Path,
5197db96d56Sopenharmony_ci        default=None,
5207db96d56Sopenharmony_ci    )
5217db96d56Sopenharmony_ci    parser.add_argument(
5227db96d56Sopenharmony_ci        "--catalog",
5237db96d56Sopenharmony_ci        metavar="file",
5247db96d56Sopenharmony_ci        help="The CDF file to write catalog entries to",
5257db96d56Sopenharmony_ci        type=Path,
5267db96d56Sopenharmony_ci        default=None,
5277db96d56Sopenharmony_ci    )
5287db96d56Sopenharmony_ci    parser.add_argument(
5297db96d56Sopenharmony_ci        "--log",
5307db96d56Sopenharmony_ci        metavar="file",
5317db96d56Sopenharmony_ci        help="Write all operations to the specified file",
5327db96d56Sopenharmony_ci        type=Path,
5337db96d56Sopenharmony_ci        default=None,
5347db96d56Sopenharmony_ci    )
5357db96d56Sopenharmony_ci    parser.add_argument(
5367db96d56Sopenharmony_ci        "-t",
5377db96d56Sopenharmony_ci        "--temp",
5387db96d56Sopenharmony_ci        metavar="file",
5397db96d56Sopenharmony_ci        help="A temporary working directory",
5407db96d56Sopenharmony_ci        type=Path,
5417db96d56Sopenharmony_ci        default=None,
5427db96d56Sopenharmony_ci    )
5437db96d56Sopenharmony_ci    parser.add_argument(
5447db96d56Sopenharmony_ci        "-d", "--debug", help="Include debug build", action="store_true"
5457db96d56Sopenharmony_ci    )
5467db96d56Sopenharmony_ci    parser.add_argument(
5477db96d56Sopenharmony_ci        "-p",
5487db96d56Sopenharmony_ci        "--precompile",
5497db96d56Sopenharmony_ci        help="Include .pyc files instead of .py",
5507db96d56Sopenharmony_ci        action="store_true",
5517db96d56Sopenharmony_ci    )
5527db96d56Sopenharmony_ci    parser.add_argument(
5537db96d56Sopenharmony_ci        "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true"
5547db96d56Sopenharmony_ci    )
5557db96d56Sopenharmony_ci    parser.add_argument(
5567db96d56Sopenharmony_ci        "--flat-dlls", help="Does not create a DLLs directory", action="store_true"
5577db96d56Sopenharmony_ci    )
5587db96d56Sopenharmony_ci    parser.add_argument(
5597db96d56Sopenharmony_ci        "-a",
5607db96d56Sopenharmony_ci        "--include-all",
5617db96d56Sopenharmony_ci        help="Include all optional components",
5627db96d56Sopenharmony_ci        action="store_true",
5637db96d56Sopenharmony_ci    )
5647db96d56Sopenharmony_ci    parser.add_argument(
5657db96d56Sopenharmony_ci        "--include-cat",
5667db96d56Sopenharmony_ci        metavar="file",
5677db96d56Sopenharmony_ci        help="Specify the catalog file to include",
5687db96d56Sopenharmony_ci        type=Path,
5697db96d56Sopenharmony_ci        default=None,
5707db96d56Sopenharmony_ci    )
5717db96d56Sopenharmony_ci    for opt, help in get_argparse_options():
5727db96d56Sopenharmony_ci        parser.add_argument(opt, help=help, action="store_true")
5737db96d56Sopenharmony_ci
5747db96d56Sopenharmony_ci    ns = parser.parse_args()
5757db96d56Sopenharmony_ci    update_presets(ns)
5767db96d56Sopenharmony_ci
5777db96d56Sopenharmony_ci    ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent)
5787db96d56Sopenharmony_ci    ns.build = ns.build or Path(sys.executable).parent
5797db96d56Sopenharmony_ci    ns.temp = ns.temp or Path(tempfile.mkdtemp())
5807db96d56Sopenharmony_ci    ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build")
5817db96d56Sopenharmony_ci    if not ns.source.is_absolute():
5827db96d56Sopenharmony_ci        ns.source = (Path.cwd() / ns.source).resolve()
5837db96d56Sopenharmony_ci    if not ns.build.is_absolute():
5847db96d56Sopenharmony_ci        ns.build = (Path.cwd() / ns.build).resolve()
5857db96d56Sopenharmony_ci    if not ns.temp.is_absolute():
5867db96d56Sopenharmony_ci        ns.temp = (Path.cwd() / ns.temp).resolve()
5877db96d56Sopenharmony_ci    if not ns.doc_build.is_absolute():
5887db96d56Sopenharmony_ci        ns.doc_build = (Path.cwd() / ns.doc_build).resolve()
5897db96d56Sopenharmony_ci    if ns.include_cat and not ns.include_cat.is_absolute():
5907db96d56Sopenharmony_ci        ns.include_cat = (Path.cwd() / ns.include_cat).resolve()
5917db96d56Sopenharmony_ci    if not ns.arch:
5927db96d56Sopenharmony_ci        ns.arch = "amd64" if sys.maxsize > 2 ** 32 else "win32"
5937db96d56Sopenharmony_ci
5947db96d56Sopenharmony_ci    if ns.copy and not ns.copy.is_absolute():
5957db96d56Sopenharmony_ci        ns.copy = (Path.cwd() / ns.copy).resolve()
5967db96d56Sopenharmony_ci    if ns.zip and not ns.zip.is_absolute():
5977db96d56Sopenharmony_ci        ns.zip = (Path.cwd() / ns.zip).resolve()
5987db96d56Sopenharmony_ci    if ns.catalog and not ns.catalog.is_absolute():
5997db96d56Sopenharmony_ci        ns.catalog = (Path.cwd() / ns.catalog).resolve()
6007db96d56Sopenharmony_ci
6017db96d56Sopenharmony_ci    configure_logger(ns)
6027db96d56Sopenharmony_ci
6037db96d56Sopenharmony_ci    log_info(
6047db96d56Sopenharmony_ci        """OPTIONS
6057db96d56Sopenharmony_ciSource: {ns.source}
6067db96d56Sopenharmony_ciBuild:  {ns.build}
6077db96d56Sopenharmony_ciTemp:   {ns.temp}
6087db96d56Sopenharmony_ciArch:   {ns.arch}
6097db96d56Sopenharmony_ci
6107db96d56Sopenharmony_ciCopy to: {ns.copy}
6117db96d56Sopenharmony_ciZip to:  {ns.zip}
6127db96d56Sopenharmony_ciCatalog: {ns.catalog}""",
6137db96d56Sopenharmony_ci        ns=ns,
6147db96d56Sopenharmony_ci    )
6157db96d56Sopenharmony_ci
6167db96d56Sopenharmony_ci    if ns.arch not in ("win32", "amd64", "arm32", "arm64"):
6177db96d56Sopenharmony_ci        log_error("--arch is not a valid value (win32, amd64, arm32, arm64)")
6187db96d56Sopenharmony_ci        return 4
6197db96d56Sopenharmony_ci    if ns.arch in ("arm32", "arm64"):
6207db96d56Sopenharmony_ci        for n in ("include_idle", "include_tcltk"):
6217db96d56Sopenharmony_ci            if getattr(ns, n):
6227db96d56Sopenharmony_ci                log_warning(f"Disabling --{n.replace('_', '-')} on unsupported platform")
6237db96d56Sopenharmony_ci                setattr(ns, n, False)
6247db96d56Sopenharmony_ci
6257db96d56Sopenharmony_ci    if ns.include_idle and not ns.include_tcltk:
6267db96d56Sopenharmony_ci        log_warning("Assuming --include-tcltk to support --include-idle")
6277db96d56Sopenharmony_ci        ns.include_tcltk = True
6287db96d56Sopenharmony_ci
6297db96d56Sopenharmony_ci    try:
6307db96d56Sopenharmony_ci        generate_source_files(ns)
6317db96d56Sopenharmony_ci        files = list(get_layout(ns))
6327db96d56Sopenharmony_ci        copy_files(files, ns)
6337db96d56Sopenharmony_ci    except KeyboardInterrupt:
6347db96d56Sopenharmony_ci        log_info("Interrupted by Ctrl+C")
6357db96d56Sopenharmony_ci        return 3
6367db96d56Sopenharmony_ci    except SystemExit:
6377db96d56Sopenharmony_ci        raise
6387db96d56Sopenharmony_ci    except:
6397db96d56Sopenharmony_ci        log_exception("Unhandled error")
6407db96d56Sopenharmony_ci
6417db96d56Sopenharmony_ci    if error_was_logged():
6427db96d56Sopenharmony_ci        log_error("Errors occurred.")
6437db96d56Sopenharmony_ci        return 1
6447db96d56Sopenharmony_ci
6457db96d56Sopenharmony_ci
6467db96d56Sopenharmony_ciif __name__ == "__main__":
6477db96d56Sopenharmony_ci    sys.exit(int(main() or 0))
648