mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			519 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			519 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """
 | |
| 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/idlex150.png",
 | |
|     Square44x44Logo="_resources/idlex44.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
 | |
| 
 | |
|     try:
 | |
|         winver = tuple(int(i) for i in os.getenv("APPX_DATA_WINVER", "").split(".", maxsplit=3))
 | |
|     except (TypeError, ValueError):
 | |
|         winver = ()
 | |
| 
 | |
|     # Default "known good" version is 10.0.22000, first Windows 11 release
 | |
|     winver = winver or (10, 0, 22000)
 | |
| 
 | |
|     if winver < (10, 0, 17763):
 | |
|         winver = 10, 0, 17763
 | |
|     find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set(
 | |
|         "MaxVersionTested", "{}.{}.{}.{}".format(*(winver + (0, 0, 0, 0)[:4]))
 | |
|     )
 | |
| 
 | |
|     # Only for Python 3.11 and later. Older versions do not disable virtualization
 | |
|     if (VER_MAJOR, VER_MINOR) >= (3, 11) and 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
 | |
|     if ns.include_idle and ns.include_launchers:
 | |
|         for px in [44, 150]:
 | |
|             src = icons / "idlex{}.png".format(px)
 | |
|             yield f"_resources/idlex{px}.png", src
 | |
|             yield f"_resources/idlex{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
 | 
