diff options
Diffstat (limited to 'PC/layout/support/appxmanifest.py')
-rw-r--r-- | PC/layout/support/appxmanifest.py | 506 |
1 files changed, 0 insertions, 506 deletions
diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py deleted file mode 100644 index 9a7439d..0000000 --- a/PC/layout/support/appxmanifest.py +++ /dev/null @@ -1,506 +0,0 @@ -""" -File generation for APPX/MSIX manifests. -""" - -__author__ = "Steve Dower <steve.dower@python.org>" -__version__ = "3.8" - - -import collections -import ctypes -import io -import os -import sys - -from pathlib import Path, PureWindowsPath -from xml.etree import ElementTree as ET - -from .constants import * - -__all__ = ["get_appx_layout"] - - -APPX_DATA = dict( - Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT), - Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3), - Publisher=os.getenv( - "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B" - ), - DisplayName="Python {}".format(VER_DOT), - Description="The Python {} runtime and console.".format(VER_DOT), -) - -APPX_PLATFORM_DATA = dict( - _keys=("ProcessorArchitecture",), - win32=("x86",), - amd64=("x64",), - arm32=("arm",), - arm64=("arm64",), -) - -PYTHON_VE_DATA = dict( - DisplayName="Python {}".format(VER_DOT), - Description="Python interactive console", - Square150x150Logo="_resources/pythonx150.png", - Square44x44Logo="_resources/pythonx44.png", - BackgroundColor="transparent", -) - -PYTHONW_VE_DATA = dict( - DisplayName="Python {} (Windowed)".format(VER_DOT), - Description="Python windowed app launcher", - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", - BackgroundColor="transparent", - AppListEntry="none", -) - -PIP_VE_DATA = dict( - DisplayName="pip (Python {})".format(VER_DOT), - Description="pip package manager for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonx150.png", - Square44x44Logo="_resources/pythonx44.png", - BackgroundColor="transparent", - AppListEntry="none", -) - -IDLE_VE_DATA = dict( - DisplayName="IDLE (Python {})".format(VER_DOT), - Description="IDLE editor for Python {}".format(VER_DOT), - Square150x150Logo="_resources/pythonwx150.png", - Square44x44Logo="_resources/pythonwx44.png", - BackgroundColor="transparent", -) - -PY_PNG = "_resources/py.png" - -APPXMANIFEST_NS = { - "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", - "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10", - "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10", - "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities", - "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4", - "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4", - "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6", - "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3", - "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4", - "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5", -} - -APPXMANIFEST_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?> -<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" - xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" - xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" - xmlns:rescap4="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4" - xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" - xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4" - xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"> - <Identity Name="" - Version="" - Publisher="" - ProcessorArchitecture="" /> - <Properties> - <DisplayName></DisplayName> - <PublisherDisplayName>Python Software Foundation</PublisherDisplayName> - <Description></Description> - <Logo>_resources/pythonx50.png</Logo> - </Properties> - <Resources> - <Resource Language="en-US" /> - </Resources> - <Dependencies> - <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="" /> - </Dependencies> - <Capabilities> - <rescap:Capability Name="runFullTrust"/> - </Capabilities> - <Applications> - </Applications> - <Extensions> - </Extensions> -</Package>""" - - -RESOURCES_XML_TEMPLATE = r"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<!--This file is input for makepri.exe. It should be excluded from the final package.--> -<resources targetOsVersion="10.0.0" majorVersion="1"> - <packaging> - <autoResourcePackage qualifier="Language"/> - <autoResourcePackage qualifier="Scale"/> - <autoResourcePackage qualifier="DXFeatureLevel"/> - </packaging> - <index root="\" startIndexAt="\"> - <default> - <qualifier name="Language" value="en-US"/> - <qualifier name="Contrast" value="standard"/> - <qualifier name="Scale" value="100"/> - <qualifier name="HomeRegion" value="001"/> - <qualifier name="TargetSize" value="256"/> - <qualifier name="LayoutDirection" value="LTR"/> - <qualifier name="Theme" value="dark"/> - <qualifier name="AlternateForm" value=""/> - <qualifier name="DXFeatureLevel" value="DX9"/> - <qualifier name="Configuration" value=""/> - <qualifier name="DeviceFamily" value="Universal"/> - <qualifier name="Custom" value=""/> - </default> - <indexer-config type="folder" foldernameAsQualifier="true" filenameAsQualifier="true" qualifierDelimiter="$"/> - <indexer-config type="resw" convertDotsToSlashes="true" initialPath=""/> - <indexer-config type="resjson" initialPath=""/> - <indexer-config type="PRI"/> - </index> -</resources>""" - - -SCCD_FILENAME = "PC/classicAppCompat.sccd" - -SPECIAL_LOOKUP = object() - -REGISTRY = { - "HKCU\\Software\\Python\\PythonCore": { - VER_DOT: { - "DisplayName": APPX_DATA["DisplayName"], - "SupportUrl": "https://www.python.org/", - "SysArchitecture": SPECIAL_LOOKUP, - "SysVersion": VER_DOT, - "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO), - "InstallPath": { - "": "[{AppVPackageRoot}]", - "ExecutablePath": "[{{AppVPackageRoot}}]\\python{}.exe".format(VER_DOT), - "WindowedExecutablePath": "[{{AppVPackageRoot}}]\\pythonw{}.exe".format( - VER_DOT - ), - }, - "Help": { - "Main Python Documentation": { - "_condition": lambda ns: ns.include_chm, - "": "[{{AppVPackageRoot}}]\\Doc\\{}".format(PYTHON_CHM_NAME), - }, - "Local Python Documentation": { - "_condition": lambda ns: ns.include_html_doc, - "": "[{AppVPackageRoot}]\\Doc\\html\\index.html", - }, - "Online Python Documentation": { - "": "https://docs.python.org/{}".format(VER_DOT) - }, - }, - "Idle": { - "_condition": lambda ns: ns.include_idle, - "": "[{AppVPackageRoot}]\\Lib\\idlelib\\idle.pyw", - }, - } - } -} - - -def get_packagefamilyname(name, publisher_id): - class PACKAGE_ID(ctypes.Structure): - _fields_ = [ - ("reserved", ctypes.c_uint32), - ("processorArchitecture", ctypes.c_uint32), - ("version", ctypes.c_uint64), - ("name", ctypes.c_wchar_p), - ("publisher", ctypes.c_wchar_p), - ("resourceId", ctypes.c_wchar_p), - ("publisherId", ctypes.c_wchar_p), - ] - _pack_ = 4 - - pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None) - result = ctypes.create_unicode_buffer(256) - result_len = ctypes.c_uint32(256) - r = ctypes.windll.kernel32.PackageFamilyNameFromId( - pid, ctypes.byref(result_len), result - ) - if r: - raise OSError(r, "failed to get package family name") - return result.value[: result_len.value] - - -def _fixup_sccd(ns, sccd, new_hash=None): - if not new_hash: - return sccd - - NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd") - with open(sccd, "rb") as f: - xml = ET.parse(f) - - pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"]) - - ae = xml.find("s:AuthorizedEntities", NS) - ae.clear() - - e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity")) - e.set("AppPackageFamilyName", pfn) - e.set("CertificateSignatureHash", new_hash) - - for e in xml.findall("s:Catalog", NS): - e.text = "FFFF" - - sccd = ns.temp / sccd.name - sccd.parent.mkdir(parents=True, exist_ok=True) - with open(sccd, "wb") as f: - xml.write(f, encoding="utf-8") - - return sccd - - -def find_or_add(xml, element, attr=None, always_add=False): - if always_add: - e = None - else: - q = element - if attr: - q += "[@{}='{}']".format(*attr) - e = xml.find(q, APPXMANIFEST_NS) - if e is None: - prefix, _, name = element.partition(":") - name = ET.QName(APPXMANIFEST_NS[prefix or ""], name) - e = ET.SubElement(xml, name) - if attr: - e.set(*attr) - return e - - -def _get_app(xml, appid): - if appid: - app = xml.find( - "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS - ) - if app is None: - raise LookupError(appid) - else: - app = xml - return app - - -def add_visual(xml, appid, data): - app = _get_app(xml, appid) - e = find_or_add(app, "uap:VisualElements") - for i in data.items(): - e.set(*i) - return e - - -def add_alias(xml, appid, alias, subsystem="windows"): - app = _get_app(xml, appid) - e = find_or_add(app, "m:Extensions") - e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias")) - e = find_or_add(e, "uap5:AppExecutionAlias") - e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem) - e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias)) - - -def add_file_type(xml, appid, name, suffix, parameters='"%1"', info=None, logo=None): - app = _get_app(xml, appid) - e = find_or_add(app, "m:Extensions") - e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation")) - e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name)) - e.set("Parameters", parameters) - if info: - find_or_add(e, "uap:DisplayName").text = info - if logo: - find_or_add(e, "uap:Logo").text = logo - e = find_or_add(e, "uap:SupportedFileTypes") - if isinstance(suffix, str): - suffix = [suffix] - for s in suffix: - ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s - - -def add_application( - ns, xml, appid, executable, aliases, visual_element, subsystem, file_types -): - node = xml.find("m:Applications", APPXMANIFEST_NS) - suffix = "_d.exe" if ns.debug else ".exe" - app = ET.SubElement( - node, - ET.QName(APPXMANIFEST_NS[""], "Application"), - { - "Id": appid, - "Executable": executable + suffix, - "EntryPoint": "Windows.FullTrustApplication", - ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true", - }, - ) - if visual_element: - add_visual(app, None, visual_element) - for alias in aliases: - add_alias(app, None, alias + suffix, subsystem) - if file_types: - add_file_type(app, None, *file_types) - return app - - -def _get_registry_entries(ns, root="", d=None): - r = root if root else PureWindowsPath("") - if d is None: - d = REGISTRY - for key, value in d.items(): - if key == "_condition": - continue - if value is SPECIAL_LOOKUP: - if key == "SysArchitecture": - value = { - "win32": "32bit", - "amd64": "64bit", - "arm32": "32bit", - "arm64": "64bit", - }[ns.arch] - else: - raise ValueError(f"Key '{key}' unhandled for special lookup") - if isinstance(value, dict): - cond = value.get("_condition") - if cond and not cond(ns): - continue - fullkey = r - for part in PureWindowsPath(key).parts: - fullkey /= part - if len(fullkey.parts) > 1: - yield str(fullkey), None, None - yield from _get_registry_entries(ns, fullkey, value) - elif len(r.parts) > 1: - yield str(r), key, value - - -def add_registry_entries(ns, xml): - e = find_or_add(xml, "m:Extensions") - e = find_or_add(e, "rescap4:Extension") - e.set("Category", "windows.classicAppCompatKeys") - e.set("EntryPoint", "Windows.FullTrustApplication") - e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys")) - for name, valuename, value in _get_registry_entries(ns): - k = ET.SubElement( - e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey") - ) - k.set("Name", name) - if value: - k.set("ValueName", valuename) - k.set("Value", value) - k.set("ValueType", "REG_SZ") - - -def disable_registry_virtualization(xml): - e = find_or_add(xml, "m:Properties") - e = find_or_add(e, "desktop6:RegistryWriteVirtualization") - e.text = "disabled" - e = find_or_add(xml, "m:Capabilities") - e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources")) - - -def get_appxmanifest(ns): - for k, v in APPXMANIFEST_NS.items(): - ET.register_namespace(k, v) - ET.register_namespace("", APPXMANIFEST_NS["m"]) - - xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE)) - NS = APPXMANIFEST_NS - QN = ET.QName - - data = dict(APPX_DATA) - for k, v in zip(APPX_PLATFORM_DATA["_keys"], APPX_PLATFORM_DATA[ns.arch]): - data[k] = v - - node = xml.find("m:Identity", NS) - for k in node.keys(): - value = data.get(k) - if value: - node.set(k, value) - - for node in xml.find("m:Properties", NS): - value = data.get(node.tag.rpartition("}")[2]) - if value: - node.text = value - - winver = sys.getwindowsversion()[:3] - if winver < (10, 0, 17763): - winver = 10, 0, 17763 - find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set( - "MaxVersionTested", "{}.{}.{}.0".format(*winver) - ) - - if winver > (10, 0, 17763): - disable_registry_virtualization(xml) - - app = add_application( - ns, - xml, - "Python", - "python{}".format(VER_DOT), - ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)], - PYTHON_VE_DATA, - "console", - ("python.file", [".py"], '"%1"', "Python File", PY_PNG), - ) - - add_application( - ns, - xml, - "PythonW", - "pythonw{}".format(VER_DOT), - ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)], - PYTHONW_VE_DATA, - "windows", - ("python.windowedfile", [".pyw"], '"%1"', "Python File (no console)", PY_PNG), - ) - - if ns.include_pip and ns.include_launchers: - add_application( - ns, - xml, - "Pip", - "pip{}".format(VER_DOT), - ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)], - PIP_VE_DATA, - "console", - ("python.wheel", [".whl"], 'install "%1"', "Python Wheel"), - ) - - if ns.include_idle and ns.include_launchers: - add_application( - ns, - xml, - "Idle", - "idle{}".format(VER_DOT), - ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)], - IDLE_VE_DATA, - "windows", - None, - ) - - if (ns.source / SCCD_FILENAME).is_file(): - add_registry_entries(ns, xml) - node = xml.find("m:Capabilities", NS) - node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability")) - node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe") - - buffer = io.BytesIO() - xml.write(buffer, encoding="utf-8", xml_declaration=True) - return buffer.getbuffer() - - -def get_resources_xml(ns): - return RESOURCES_XML_TEMPLATE.encode("utf-8") - - -def get_appx_layout(ns): - if not ns.include_appxmanifest: - return - - yield "AppxManifest.xml", ("AppxManifest.xml", get_appxmanifest(ns)) - yield "_resources.xml", ("_resources.xml", get_resources_xml(ns)) - icons = ns.source / "PC" / "icons" - for px in [44, 50, 150]: - src = icons / "pythonx{}.png".format(px) - yield f"_resources/pythonx{px}.png", src - yield f"_resources/pythonx{px}$targetsize-{px}_altform-unplated.png", src - for px in [44, 150]: - src = icons / "pythonwx{}.png".format(px) - yield f"_resources/pythonwx{px}.png", src - yield f"_resources/pythonwx{px}$targetsize-{px}_altform-unplated.png", src - yield f"_resources/py.png", icons / "py.png" - sccd = ns.source / SCCD_FILENAME - if sccd.is_file(): - # This should only be set for side-loading purposes. - sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256")) - yield sccd.name, sccd |