From 34c120253649294f1497a83f575907f45062d12a Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 17 Jan 2025 16:14:32 +0000 Subject: [PATCH] [WIP] Add PyManager support to PC/layout --- PC/layout/main.py | 16 +++ PC/layout/support/options.py | 15 +++ PC/layout/support/pymanager.py | 171 +++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 PC/layout/support/pymanager.py diff --git a/PC/layout/main.py b/PC/layout/main.py index 8bd435456c635a..268d8325663bd0 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -8,6 +8,7 @@ __version__ = "3.8" import argparse +import json import os import shutil import sys @@ -28,6 +29,7 @@ from .support.options import * from .support.pip import * from .support.props import * +from .support.pymanager import * from .support.nuspec import * TEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_35", "_ctypes_test", "_test*") @@ -303,6 +305,9 @@ def _c(d): else: yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat + if ns.include_install_json or ns.include_install_embed_json: + yield "__install__.json", ns.temp / "__install__.json" + def _compile_one_py(src, dest, name, optimize, checked=True): import py_compile @@ -394,6 +399,17 @@ def generate_source_files(ns): log_info("Extracting pip") extract_pip_files(ns) + if ns.include_install_json: + log_info("Generating __install__.json in {}", ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / "__install__.json", "w", encoding="utf-8") as f: + json.dump(calculate_install_json(ns), f, indent=2) + elif ns.include_install_embed_json: + log_info("Generating embeddable __install__.json in {}", ns.temp) + ns.temp.mkdir(parents=True, exist_ok=True) + with open(ns.temp / "__install__.json", "w", encoding="utf-8") as f: + json.dump(calculate_install_embed_json(ns), f, indent=2) + def _create_zip_file(ns): if not ns.zip: diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py index f1a8eb0b317744..02c9d7e693fb48 100644 --- a/PC/layout/support/options.py +++ b/PC/layout/support/options.py @@ -36,6 +36,8 @@ def public(f): "alias": {"help": "aliased python.exe entry-point binaries"}, "alias3": {"help": "aliased python3.exe entry-point binaries"}, "alias3x": {"help": "aliased python3.x.exe entry-point binaries"}, + "install-json": {"help": "a PyManager __install.json__ file"}, + "install-embed-json": {"help": "a PyManager __install.json__ file for embeddable distro"}, } @@ -95,6 +97,19 @@ def public(f): "precompile", ], }, + "pymanager": { + "help": "PyManager package", + "options": [ + "stable", + "pip", + "tcltk", + "idle", + "venv", + "dev", + "html-doc", + "install-json", + ], + }, } diff --git a/PC/layout/support/pymanager.py b/PC/layout/support/pymanager.py new file mode 100644 index 00000000000000..0b1db1c1970f7c --- /dev/null +++ b/PC/layout/support/pymanager.py @@ -0,0 +1,171 @@ +from .constants import * + +FULL_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}.{VER_FIELD4}" +XYZ_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}" +FILE_VERSION = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}{VER_SUFFIX}" + +def calculate_install_json(ns, *, for_embed=False): + TARGET = "python.exe" + TARGETW = "pythonw.exe" + + SYS_ARCH = { + "win32": "32bit", + "amd64": "64bit", + "arm64": "64bit", + }[ns.arch] + TAG_ARCH = { + "win32": "-32", + "amd64": "", + "arm64": "-arm64", + }[ns.arch] + + if not for_embed: + if not ns.include_freethreaded: + ID_PREFIX = "pythoncore-" + COMPANY = "PythonCore" + TAG_SUFFIX = "" + TAG_ARCH_SUFFIX = TAG_ARCH + FILE_SUFFIX = f"-{ns.arch}" + FRIENDLY_ARCH = { + "win32": " (32-bit)", + "amd64": "", + "arm64": " (ARM64)", + }[ns.arch] + else: + ID_PREFIX = "pythoncore-" + COMPANY = "PythonCore" + TAG_SUFFIX = "t" + TAG_ARCH_SUFFIX = f"t{TAG_ARCH}" + FILE_SUFFIX = f"t-{ns.arch}" + TARGET = f"python{VER_MAJOR}.{VER_MINOR}t.exe" + TARGETW = f"pythonw{VER_MAJOR}.{VER_MINOR}t.exe" + FRIENDLY_ARCH = { + "win32": " (32-bit, freethreaded)", + "amd64": " (freethreaded)", + "arm64": " (ARM64, freethreaded)", + }[ns.arch] + else: + ID_PREFIX = "pythonembed-" + COMPANY = "PythonCore" + TAG_SUFFIX = "-embed" + TAG_ARCH_SUFFIX = f"{TAG_ARCH}-embed" + FILE_SUFFIX = f"-embed-{ns.arch}" + TARGETW = None + FRIENDLY_ARCH = { + "win32": " (32-bit, embeddable)", + "amd64": " (embeddable)", + "arm64": " (ARM64, embeddable)", + }[ns.arch] + + + FULL_TAG = f"{VER_MAJOR}.{VER_MINOR}.{VER_MICRO}{TAG_SUFFIX}" + XY_TAG = f"{VER_MAJOR}.{VER_MINOR}{TAG_SUFFIX}" + X_TAG = f"{VER_MAJOR}{TAG_SUFFIX}" + DISPLAY_VERSION = f"{XYZ_VERSION}{VER_SUFFIX}{FRIENDLY_ARCH}" + + STD_RUN_FOR = [] + STD_ALIAS = [] + STD_PEP514 = [] + STD_START = [] + + if TARGET: + STD_RUN_FOR.extend([ + {"tag": XY_TAG, "target": TARGET}, + {"tag": X_TAG, "target": TARGET}, + ]) + STD_ALIAS.extend([ + {"name": f"python{XY_TAG}.exe", "target": TARGET}, + {"name": f"python{X_TAG}.exe", "target": TARGET}, + ]) + + if TARGETW: + STD_RUN_FOR.extend([ + {"tag": XY_TAG, "target": TARGETW, "windowed": 1}, + {"tag": X_TAG, "target": TARGETW, "windowed": 1}, + ]) + STD_ALIAS.extend([ + {"name": f"pythonw{XY_TAG}.exe", "target": TARGETW, "windowed": 1}, + {"name": f"pythonw{X_TAG}.exe", "target": TARGETW, "windowed": 1} + ]) + + data = { + "schema": 1, + "id": f"{ID_PREFIX}{XY_TAG}", + "sort-version": FULL_VERSION, + "company": COMPANY, + "tag": FULL_TAG, + "install-for": [FULL_TAG, XY_TAG, X_TAG], + "run-for": STD_RUN_FOR, + "alias": STD_ALIAS, + "shortcuts": [], + "displayName": f"Python {DISPLAY_VERSION}", + "executable": f"./{TARGET}", + "url": f"https://www.python.org/ftp/python/{XYZ_VERSION}/python-{FILE_VERSION}{FILE_SUFFIX}.zip" + } + + STD_PEP514.append({ + "kind": "pep514", + "Key": rf"{COMPANY}\{XY_TAG}", + "DisplayName": f"Python {DISPLAY_VERSION}", + "SupportUrl": "https://www.python.org/", + "SysArchitecture": SYS_ARCH, + "SysVersion": VER_DOT, + "Version": FULL_VERSION, + "InstallPath": { + "": "%PREFIX%", + "ExecutablePath": f"%PREFIX%{TARGET}", + }, + "Help": { + "Online Python Documentation": { + "": f"https://docs.python.org/{VER_DOT}/" + }, + }, + }) + + STD_START.append({ + "kind": "start", + "Name": f"Python {VER_DOT}{FRIENDLY_ARCH}", + "Items": [ + { + "Name": f"Python {VER_DOT}{FRIENDLY_ARCH}", + "Target": f"%PREFIX%{TARGET}", + "Icon": f"%PREFIX%{TARGET}", + }, + { + "Name": f"Python {VER_DOT} Online Documentation", + "Icon": r"%SystemRoot%\System32\SHELL32.dll", + "IconIndex": 13, + "Target": f"https://docs.python.org/{VER_DOT}/", + }, + ], + }) + + if STD_PEP514: + if TARGETW: + STD_PEP514[0]["InstallPath"]["WindowedExecutablePath"] = f"%PREFIX%{TARGETW}" + if ns.include_html_doc: + STD_PEP514[0]["Help"]["Main Python Documentation"] = { + "": rf"%PREFIX%Doc\index.html", + } + STD_START[0]["Items"].append({ + "Name": f"Python {VER_DOT} Manuals{FRIENDLY_ARCH}", + "Target": r"%PREFIX%Doc\index.html", + }) + elif ns.include_chm: + STD_PEP514[0]["Help"]["Main Python Documentation"] = { + "": rf"%PREFIX%Doc\{PYTHON_CHM_NAME}", + } + STD_START[0]["Items"].append({ + "Name": f"Python {VER_DOT} Manuals{FRIENDLY_ARCH}", + "Target": "%WINDIR%hhc.exe", + "Arguments": rf"%PREFIX%Doc\{PYTHON_CHM_NAME}", + }) + + data["shortcuts"] = [*STD_PEP514, *STD_START] + + return data + + +def calculate_install_embed_json(ns): + return calculate_install_json(ns, for_embed=True) + \ No newline at end of file