import os
import sys
import zipfile
from pathlib import Path
from subprocess import check_call as run
from _make_helper import (
copyfile,
copytree,
get_dirs,
get_msix_version,
get_output_name,
get_sdk_bins,
rmtree,
unlink,
)
SDK_BINS = get_sdk_bins()
MAKEAPPX = SDK_BINS / "makeappx.exe"
MAKEPRI = SDK_BINS / "makepri.exe"
for tool in [MAKEAPPX, MAKEPRI]:
if not tool.is_file():
print("Unable to locate Windows Kit tool", tool.name, file=sys.stderr)
sys.exit(3)
DIRS = get_dirs()
BUILD = DIRS["build"]
TEMP = DIRS["temp"]
LAYOUT = DIRS["out"]
LAYOUT2 = TEMP / "store-layout"
SRC = DIRS["src"]
DIST = DIRS["dist"]
# Calculate output names (must be after building)
NAME = get_output_name(DIRS)
VERSION = get_msix_version(DIRS)
DIST_MSIX = DIST / f"{NAME}.msix"
DIST_STORE_MSIX = DIST / f"{NAME}-store.msix"
DIST_APPXSYM = DIST / f"{NAME}-store.appxsym"
DIST_MSIXUPLOAD = DIST / f"{NAME}-store.msixupload"
unlink(DIST_MSIX, DIST_STORE_MSIX, DIST_APPXSYM, DIST_MSIXUPLOAD)
# Generate resources info in LAYOUT
if not (LAYOUT / "_resources.pri").is_file():
run([MAKEPRI, "new", "/o",
"/pr", LAYOUT,
"/cf", SRC / "pymanager/resources.xml",
"/of", LAYOUT / "_resources.pri",
"/mf", "appx"])
# Clean up non-shipping files from LAYOUT
preserved = [
*LAYOUT.glob("pyshellext*.dll"),
]
for f in preserved:
print("Preserving", f, "as", TEMP / f.name)
copyfile(f, TEMP / f.name)
unlink(
*LAYOUT.rglob("*.pdb"),
*LAYOUT.rglob("*.pyc"),
*LAYOUT.rglob("__pycache__"),
*preserved,
)
# Package into DIST
run([MAKEAPPX, "pack", "/o", "/d", LAYOUT, "/p", DIST_MSIX])
print("Copying appinstaller file to", DIST)
copyfile(LAYOUT / "pymanager.appinstaller", DIST / "pymanager.appinstaller")
if os.getenv("PYMANAGER_APPX_STORE_PUBLISHER"):
# Clone and update layout for Store build
rmtree(LAYOUT2)
copytree(LAYOUT, LAYOUT2)
unlink(*LAYOUT2.glob("*.appinstaller"))
def patch_appx(source):
from xml.etree import ElementTree as ET
NS = {}
with open(source, "r", encoding="utf-8") as f:
NS = dict(e for _, e in ET.iterparse(f, events=("start-ns",)))
for k, v in NS.items():
ET.register_namespace(k, v)
NS["x"] = NS[""]
with open(source, "r", encoding="utf-8") as f:
xml = ET.parse(f)
identity = xml.find("x:Identity", NS)
identity.set("Publisher", os.getenv("PYMANAGER_APPX_STORE_PUBLISHER"))
p = xml.find("x:Properties", NS)
e = p.find("uap13:AutoUpdate", NS)
p.remove(e)
e = p.find(f"uap17:UpdateWhileInUse", NS)
p.remove(e)
with open(source, "wb") as f:
xml.write(f, "utf-8")
# We need to remove unused namespaces from IgnorableNamespaces.
# The easiest way to get this right is to read the file back in, see
# which namespaces were silently left out by etree, and remove those.
with open(source, "r", encoding="utf-8") as f:
NS = dict(e for _, e in ET.iterparse(f, events=("start-ns",)))
with open(source, "r", encoding="utf-8") as f:
xml = ET.parse(f)
p = xml.getroot()
p.set("IgnorableNamespaces", " ".join(s for s in p.get("IgnorableNamespaces").split() if s in NS))
with open(source, "wb") as f:
xml.write(f, "utf-8")
patch_appx(LAYOUT2 / "appxmanifest.xml")
run([MAKEAPPX, "pack", "/o", "/d", LAYOUT2, "/p", DIST_STORE_MSIX])
# Pack symbols
print("Packing symbols to", DIST_APPXSYM)
with zipfile.ZipFile(DIST_APPXSYM, "w") as zf:
for f in BUILD.rglob("*.pdb"):
zf.write(f, arcname=f.name)
# Pack upload MSIX for Store
print("Packing Store upload to", DIST_MSIXUPLOAD)
with zipfile.ZipFile(DIST_MSIXUPLOAD, "w") as zf:
zf.write(DIST_STORE_MSIX, arcname=DIST_STORE_MSIX.name)
zf.write(DIST_APPXSYM, arcname=DIST_APPXSYM.name)
for f in preserved:
print("Restoring", f, "from", TEMP / f.name)
copyfile(TEMP / f.name, f)
unlink(TEMP / f.name)