mirror of
https://github.com/python/cpython.git
synced 2025-08-03 08:34:29 +00:00
bpo-34977: Add Windows App Store package (GH-11027)
Also adds the PC/layout script for generating layouts on Windows.
This commit is contained in:
parent
1c3de541e6
commit
0cd6391fd8
47 changed files with 2928 additions and 405 deletions
0
PC/layout/__init__.py
Normal file
0
PC/layout/__init__.py
Normal file
14
PC/layout/__main__.py
Normal file
14
PC/layout/__main__.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
import sys
|
||||
|
||||
try:
|
||||
import layout
|
||||
except ImportError:
|
||||
# Failed to import our package, which likely means we were started directly
|
||||
# Add the additional search path needed to locate our module.
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from layout.main import main
|
||||
|
||||
sys.exit(int(main() or 0))
|
616
PC/layout/main.py
Normal file
616
PC/layout/main.py
Normal file
|
@ -0,0 +1,616 @@
|
|||
"""
|
||||
Generates a layout of Python for Windows from a build.
|
||||
|
||||
See python make_layout.py --help for usage.
|
||||
"""
|
||||
|
||||
__author__ = "Steve Dower <steve.dower@python.org>"
|
||||
__version__ = "3.8"
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Started directly, so enable relative imports
|
||||
__path__ = [str(Path(__file__).resolve().parent)]
|
||||
|
||||
from .support.appxmanifest import *
|
||||
from .support.catalog import *
|
||||
from .support.constants import *
|
||||
from .support.filesets import *
|
||||
from .support.logging import *
|
||||
from .support.options import *
|
||||
from .support.pip import *
|
||||
from .support.props import *
|
||||
|
||||
BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py")
|
||||
BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py"
|
||||
|
||||
TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*")
|
||||
TEST_DIRS_ONLY = FileNameSet("test", "tests")
|
||||
|
||||
IDLE_DIRS_ONLY = FileNameSet("idlelib")
|
||||
|
||||
TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter")
|
||||
TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo")
|
||||
TCLTK_FILES_ONLY = FileNameSet("turtle.py")
|
||||
|
||||
VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip")
|
||||
|
||||
EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext")
|
||||
EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle")
|
||||
EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt")
|
||||
EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*")
|
||||
EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll")
|
||||
|
||||
REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*")
|
||||
|
||||
LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt")
|
||||
|
||||
PY_FILES = FileSuffixSet(".py")
|
||||
PYC_FILES = FileSuffixSet(".pyc")
|
||||
CAT_FILES = FileSuffixSet(".cat")
|
||||
CDF_FILES = FileSuffixSet(".cdf")
|
||||
|
||||
DATA_DIRS = FileNameSet("data")
|
||||
|
||||
TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser")
|
||||
TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt")
|
||||
|
||||
|
||||
def get_lib_layout(ns):
|
||||
def _c(f):
|
||||
if f in EXCLUDE_FROM_LIB:
|
||||
return False
|
||||
if f.is_dir():
|
||||
if f in TEST_DIRS_ONLY:
|
||||
return ns.include_tests
|
||||
if f in TCLTK_DIRS_ONLY:
|
||||
return ns.include_tcltk
|
||||
if f in IDLE_DIRS_ONLY:
|
||||
return ns.include_idle
|
||||
if f in VENV_DIRS_ONLY:
|
||||
return ns.include_venv
|
||||
else:
|
||||
if f in TCLTK_FILES_ONLY:
|
||||
return ns.include_tcltk
|
||||
if f in BDIST_WININST_FILES_ONLY:
|
||||
return ns.include_bdist_wininst
|
||||
return True
|
||||
|
||||
for dest, src in rglob(ns.source / "Lib", "**/*", _c):
|
||||
yield dest, src
|
||||
|
||||
if not ns.include_bdist_wininst:
|
||||
src = ns.source / BDIST_WININST_STUB
|
||||
yield Path("distutils/command/bdist_wininst.py"), src
|
||||
|
||||
|
||||
def get_tcltk_lib(ns):
|
||||
if not ns.include_tcltk:
|
||||
return
|
||||
|
||||
tcl_lib = os.getenv("TCL_LIBRARY")
|
||||
if not tcl_lib or not os.path.isdir(tcl_lib):
|
||||
try:
|
||||
with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f:
|
||||
tcl_lib = f.read().strip()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
if not tcl_lib or not os.path.isdir(tcl_lib):
|
||||
warn("Failed to find TCL_LIBRARY")
|
||||
return
|
||||
|
||||
for dest, src in rglob(Path(tcl_lib).parent, "**/*"):
|
||||
yield "tcl/{}".format(dest), src
|
||||
|
||||
|
||||
def get_layout(ns):
|
||||
def in_build(f, dest="", new_name=None):
|
||||
n, _, x = f.rpartition(".")
|
||||
n = new_name or n
|
||||
src = ns.build / f
|
||||
if ns.debug and src not in REQUIRED_DLLS:
|
||||
if not src.stem.endswith("_d"):
|
||||
src = src.parent / (src.stem + "_d" + src.suffix)
|
||||
if not n.endswith("_d"):
|
||||
n += "_d"
|
||||
f = n + "." + x
|
||||
yield dest + n + "." + x, src
|
||||
if ns.include_symbols:
|
||||
pdb = src.with_suffix(".pdb")
|
||||
if pdb.is_file():
|
||||
yield dest + n + ".pdb", pdb
|
||||
if ns.include_dev:
|
||||
lib = src.with_suffix(".lib")
|
||||
if lib.is_file():
|
||||
yield "libs/" + n + ".lib", lib
|
||||
|
||||
if ns.include_appxmanifest:
|
||||
yield from in_build("python_uwp.exe", new_name="python")
|
||||
yield from in_build("pythonw_uwp.exe", new_name="pythonw")
|
||||
else:
|
||||
yield from in_build("python.exe", new_name="python")
|
||||
yield from in_build("pythonw.exe", new_name="pythonw")
|
||||
|
||||
yield from in_build(PYTHON_DLL_NAME)
|
||||
|
||||
if ns.include_launchers and ns.include_appxmanifest:
|
||||
if ns.include_pip:
|
||||
yield from in_build("python_uwp.exe", new_name="pip")
|
||||
if ns.include_idle:
|
||||
yield from in_build("pythonw_uwp.exe", new_name="idle")
|
||||
|
||||
if ns.include_stable:
|
||||
yield from in_build(PYTHON_STABLE_DLL_NAME)
|
||||
|
||||
for dest, src in rglob(ns.build, "vcruntime*.dll"):
|
||||
yield dest, src
|
||||
|
||||
for dest, src in rglob(ns.build, ("*.pyd", "*.dll")):
|
||||
if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS:
|
||||
continue
|
||||
if src in EXCLUDE_FROM_PYDS:
|
||||
continue
|
||||
if src in TEST_PYDS_ONLY and not ns.include_tests:
|
||||
continue
|
||||
if src in TCLTK_PYDS_ONLY and not ns.include_tcltk:
|
||||
continue
|
||||
|
||||
yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/")
|
||||
|
||||
if ns.zip_lib:
|
||||
zip_name = PYTHON_ZIP_NAME
|
||||
yield zip_name, ns.temp / zip_name
|
||||
else:
|
||||
for dest, src in get_lib_layout(ns):
|
||||
yield "Lib/{}".format(dest), src
|
||||
|
||||
if ns.include_venv:
|
||||
yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python")
|
||||
yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw")
|
||||
|
||||
if ns.include_tools:
|
||||
|
||||
def _c(d):
|
||||
if d.is_dir():
|
||||
return d in TOOLS_DIRS
|
||||
return d in TOOLS_FILES
|
||||
|
||||
for dest, src in rglob(ns.source / "Tools", "**/*", _c):
|
||||
yield "Tools/{}".format(dest), src
|
||||
|
||||
if ns.include_underpth:
|
||||
yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME
|
||||
|
||||
if ns.include_dev:
|
||||
|
||||
def _c(d):
|
||||
if d.is_dir():
|
||||
return d.name != "internal"
|
||||
return True
|
||||
|
||||
for dest, src in rglob(ns.source / "Include", "**/*.h", _c):
|
||||
yield "include/{}".format(dest), src
|
||||
src = ns.source / "PC" / "pyconfig.h"
|
||||
yield "include/pyconfig.h", src
|
||||
|
||||
for dest, src in get_tcltk_lib(ns):
|
||||
yield dest, src
|
||||
|
||||
if ns.include_pip:
|
||||
pip_dir = get_pip_dir(ns)
|
||||
if not pip_dir.is_dir():
|
||||
log_warning("Failed to find {} - pip will not be included", pip_dir)
|
||||
else:
|
||||
pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}"
|
||||
for dest, src in rglob(pip_dir, "**/*"):
|
||||
if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB:
|
||||
continue
|
||||
yield pkg_root.format(dest), src
|
||||
|
||||
if ns.include_chm:
|
||||
for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME):
|
||||
yield "Doc/{}".format(dest), src
|
||||
|
||||
if ns.include_html_doc:
|
||||
for dest, src in rglob(ns.doc_build / "html", "**/*"):
|
||||
yield "Doc/html/{}".format(dest), src
|
||||
|
||||
if ns.include_props:
|
||||
for dest, src in get_props_layout(ns):
|
||||
yield dest, src
|
||||
|
||||
for dest, src in get_appx_layout(ns):
|
||||
yield dest, src
|
||||
|
||||
if ns.include_cat:
|
||||
if ns.flat_dlls:
|
||||
yield ns.include_cat.name, ns.include_cat
|
||||
else:
|
||||
yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat
|
||||
|
||||
|
||||
def _compile_one_py(src, dest, name, optimize):
|
||||
import py_compile
|
||||
|
||||
if dest is not None:
|
||||
dest = str(dest)
|
||||
|
||||
try:
|
||||
return Path(
|
||||
py_compile.compile(
|
||||
str(src),
|
||||
dest,
|
||||
str(name),
|
||||
doraise=True,
|
||||
optimize=optimize,
|
||||
invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
|
||||
)
|
||||
)
|
||||
except py_compile.PyCompileError:
|
||||
log_warning("Failed to compile {}", src)
|
||||
return None
|
||||
|
||||
|
||||
def _py_temp_compile(src, ns, dest_dir=None):
|
||||
if not ns.precompile or src not in PY_FILES or src.parent in DATA_DIRS:
|
||||
return None
|
||||
|
||||
dest = (dest_dir or ns.temp) / (src.stem + ".py")
|
||||
return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2)
|
||||
|
||||
|
||||
def _write_to_zip(zf, dest, src, ns):
|
||||
pyc = _py_temp_compile(src, ns)
|
||||
if pyc:
|
||||
try:
|
||||
zf.write(str(pyc), dest.with_suffix(".pyc"))
|
||||
finally:
|
||||
try:
|
||||
pyc.unlink()
|
||||
except:
|
||||
log_exception("Failed to delete {}", pyc)
|
||||
return
|
||||
|
||||
if src in LIB2TO3_GRAMMAR_FILES:
|
||||
from lib2to3.pgen2.driver import load_grammar
|
||||
|
||||
tmp = ns.temp / src.name
|
||||
try:
|
||||
shutil.copy(src, tmp)
|
||||
load_grammar(str(tmp))
|
||||
for f in ns.temp.glob(src.stem + "*.pickle"):
|
||||
zf.write(str(f), str(dest.parent / f.name))
|
||||
try:
|
||||
f.unlink()
|
||||
except:
|
||||
log_exception("Failed to delete {}", f)
|
||||
except:
|
||||
log_exception("Failed to compile {}", src)
|
||||
finally:
|
||||
try:
|
||||
tmp.unlink()
|
||||
except:
|
||||
log_exception("Failed to delete {}", tmp)
|
||||
|
||||
zf.write(str(src), str(dest))
|
||||
|
||||
|
||||
def generate_source_files(ns):
|
||||
if ns.zip_lib:
|
||||
zip_name = PYTHON_ZIP_NAME
|
||||
zip_path = ns.temp / zip_name
|
||||
if zip_path.is_file():
|
||||
zip_path.unlink()
|
||||
elif zip_path.is_dir():
|
||||
log_error(
|
||||
"Cannot create zip file because a directory exists by the same name"
|
||||
)
|
||||
return
|
||||
log_info("Generating {} in {}", zip_name, ns.temp)
|
||||
ns.temp.mkdir(parents=True, exist_ok=True)
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
for dest, src in get_lib_layout(ns):
|
||||
_write_to_zip(zf, dest, src, ns)
|
||||
|
||||
if ns.include_underpth:
|
||||
log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp)
|
||||
ns.temp.mkdir(parents=True, exist_ok=True)
|
||||
with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f:
|
||||
if ns.zip_lib:
|
||||
print(PYTHON_ZIP_NAME, file=f)
|
||||
if ns.include_pip:
|
||||
print("packages", file=f)
|
||||
else:
|
||||
print("Lib", file=f)
|
||||
print("Lib/site-packages", file=f)
|
||||
if not ns.flat_dlls:
|
||||
print("DLLs", file=f)
|
||||
print(".", file=f)
|
||||
print(file=f)
|
||||
print("# Uncomment to run site.main() automatically", file=f)
|
||||
print("#import site", file=f)
|
||||
|
||||
if ns.include_appxmanifest:
|
||||
log_info("Generating AppxManifest.xml in {}", ns.temp)
|
||||
ns.temp.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(ns.temp / "AppxManifest.xml", "wb") as f:
|
||||
f.write(get_appxmanifest(ns))
|
||||
|
||||
with open(ns.temp / "_resources.xml", "wb") as f:
|
||||
f.write(get_resources_xml(ns))
|
||||
|
||||
if ns.include_pip:
|
||||
pip_dir = get_pip_dir(ns)
|
||||
if not (pip_dir / "pip").is_dir():
|
||||
log_info("Extracting pip to {}", pip_dir)
|
||||
pip_dir.mkdir(parents=True, exist_ok=True)
|
||||
extract_pip_files(ns)
|
||||
|
||||
if ns.include_props:
|
||||
log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp)
|
||||
ns.temp.mkdir(parents=True, exist_ok=True)
|
||||
with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f:
|
||||
f.write(get_props(ns))
|
||||
|
||||
|
||||
def _create_zip_file(ns):
|
||||
if not ns.zip:
|
||||
return None
|
||||
|
||||
if ns.zip.is_file():
|
||||
try:
|
||||
ns.zip.unlink()
|
||||
except OSError:
|
||||
log_exception("Unable to remove {}", ns.zip)
|
||||
sys.exit(8)
|
||||
elif ns.zip.is_dir():
|
||||
log_error("Cannot create ZIP file because {} is a directory", ns.zip)
|
||||
sys.exit(8)
|
||||
|
||||
ns.zip.parent.mkdir(parents=True, exist_ok=True)
|
||||
return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED)
|
||||
|
||||
|
||||
def copy_files(files, ns):
|
||||
if ns.copy:
|
||||
ns.copy.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
total = len(files)
|
||||
except TypeError:
|
||||
total = None
|
||||
count = 0
|
||||
|
||||
zip_file = _create_zip_file(ns)
|
||||
try:
|
||||
need_compile = []
|
||||
in_catalog = []
|
||||
|
||||
for dest, src in files:
|
||||
count += 1
|
||||
if count % 10 == 0:
|
||||
if total:
|
||||
log_info("Processed {:>4} of {} files", count, total)
|
||||
else:
|
||||
log_info("Processed {} files", count)
|
||||
log_debug("Processing {!s}", src)
|
||||
|
||||
if (
|
||||
ns.precompile
|
||||
and src in PY_FILES
|
||||
and src not in EXCLUDE_FROM_COMPILE
|
||||
and src.parent not in DATA_DIRS
|
||||
and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib"))
|
||||
):
|
||||
if ns.copy:
|
||||
need_compile.append((dest, ns.copy / dest))
|
||||
else:
|
||||
(ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(src, ns.temp / "Lib" / dest)
|
||||
need_compile.append((dest, ns.temp / "Lib" / dest))
|
||||
|
||||
if src not in EXCLUDE_FROM_CATALOG:
|
||||
in_catalog.append((src.name, src))
|
||||
|
||||
if ns.copy:
|
||||
log_debug("Copy {} -> {}", src, ns.copy / dest)
|
||||
(ns.copy / dest).parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
shutil.copy2(src, ns.copy / dest)
|
||||
except shutil.SameFileError:
|
||||
pass
|
||||
|
||||
if ns.zip:
|
||||
log_debug("Zip {} into {}", src, ns.zip)
|
||||
zip_file.write(src, str(dest))
|
||||
|
||||
if need_compile:
|
||||
for dest, src in need_compile:
|
||||
compiled = [
|
||||
_compile_one_py(src, None, dest, optimize=0),
|
||||
_compile_one_py(src, None, dest, optimize=1),
|
||||
_compile_one_py(src, None, dest, optimize=2),
|
||||
]
|
||||
for c in compiled:
|
||||
if not c:
|
||||
continue
|
||||
cdest = Path(dest).parent / Path(c).relative_to(src.parent)
|
||||
if ns.zip:
|
||||
log_debug("Zip {} into {}", c, ns.zip)
|
||||
zip_file.write(c, str(cdest))
|
||||
in_catalog.append((cdest.name, cdest))
|
||||
|
||||
if ns.catalog:
|
||||
# Just write out the CDF now. Compilation and signing is
|
||||
# an extra step
|
||||
log_info("Generating {}", ns.catalog)
|
||||
ns.catalog.parent.mkdir(parents=True, exist_ok=True)
|
||||
write_catalog(ns.catalog, in_catalog)
|
||||
|
||||
finally:
|
||||
if zip_file:
|
||||
zip_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-v", help="Increase verbosity", action="count")
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--source",
|
||||
metavar="dir",
|
||||
help="The directory containing the repository root",
|
||||
type=Path,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b", "--build", metavar="dir", help="Specify the build directory", type=Path
|
||||
)
|
||||
parser.add_argument(
|
||||
"--doc-build",
|
||||
metavar="dir",
|
||||
help="Specify the docs build directory",
|
||||
type=Path,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--copy",
|
||||
metavar="directory",
|
||||
help="The name of the directory to copy an extracted layout to",
|
||||
type=Path,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--zip",
|
||||
metavar="file",
|
||||
help="The ZIP file to write all files to",
|
||||
type=Path,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--catalog",
|
||||
metavar="file",
|
||||
help="The CDF file to write catalog entries to",
|
||||
type=Path,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log",
|
||||
metavar="file",
|
||||
help="Write all operations to the specified file",
|
||||
type=Path,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--temp",
|
||||
metavar="file",
|
||||
help="A temporary working directory",
|
||||
type=Path,
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--debug", help="Include debug build", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--precompile",
|
||||
help="Include .pyc files instead of .py",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-z", "--zip-lib", help="Include library in a ZIP file", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--flat-dlls", help="Does not create a DLLs directory", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--include-all",
|
||||
help="Include all optional components",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--include-cat",
|
||||
metavar="file",
|
||||
help="Specify the catalog file to include",
|
||||
type=Path,
|
||||
default=None,
|
||||
)
|
||||
for opt, help in get_argparse_options():
|
||||
parser.add_argument(opt, help=help, action="store_true")
|
||||
|
||||
ns = parser.parse_args()
|
||||
update_presets(ns)
|
||||
|
||||
ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent)
|
||||
ns.build = ns.build or Path(sys.executable).parent
|
||||
ns.temp = ns.temp or Path(tempfile.mkdtemp())
|
||||
ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build")
|
||||
if not ns.source.is_absolute():
|
||||
ns.source = (Path.cwd() / ns.source).resolve()
|
||||
if not ns.build.is_absolute():
|
||||
ns.build = (Path.cwd() / ns.build).resolve()
|
||||
if not ns.temp.is_absolute():
|
||||
ns.temp = (Path.cwd() / ns.temp).resolve()
|
||||
if not ns.doc_build.is_absolute():
|
||||
ns.doc_build = (Path.cwd() / ns.doc_build).resolve()
|
||||
if ns.include_cat and not ns.include_cat.is_absolute():
|
||||
ns.include_cat = (Path.cwd() / ns.include_cat).resolve()
|
||||
|
||||
if ns.copy and not ns.copy.is_absolute():
|
||||
ns.copy = (Path.cwd() / ns.copy).resolve()
|
||||
if ns.zip and not ns.zip.is_absolute():
|
||||
ns.zip = (Path.cwd() / ns.zip).resolve()
|
||||
if ns.catalog and not ns.catalog.is_absolute():
|
||||
ns.catalog = (Path.cwd() / ns.catalog).resolve()
|
||||
|
||||
configure_logger(ns)
|
||||
|
||||
log_info(
|
||||
"""OPTIONS
|
||||
Source: {ns.source}
|
||||
Build: {ns.build}
|
||||
Temp: {ns.temp}
|
||||
|
||||
Copy to: {ns.copy}
|
||||
Zip to: {ns.zip}
|
||||
Catalog: {ns.catalog}""",
|
||||
ns=ns,
|
||||
)
|
||||
|
||||
if ns.include_idle and not ns.include_tcltk:
|
||||
log_warning("Assuming --include-tcltk to support --include-idle")
|
||||
ns.include_tcltk = True
|
||||
|
||||
try:
|
||||
generate_source_files(ns)
|
||||
files = list(get_layout(ns))
|
||||
copy_files(files, ns)
|
||||
except KeyboardInterrupt:
|
||||
log_info("Interrupted by Ctrl+C")
|
||||
return 3
|
||||
except SystemExit:
|
||||
raise
|
||||
except:
|
||||
log_exception("Unhandled error")
|
||||
|
||||
if error_was_logged():
|
||||
log_error("Errors occurred.")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(int(main() or 0))
|
0
PC/layout/support/__init__.py
Normal file
0
PC/layout/support/__init__.py
Normal file
487
PC/layout/support/appxmanifest.py
Normal file
487
PC/layout/support/appxmanifest.py
Normal file
|
@ -0,0 +1,487 @@
|
|||
"""
|
||||
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__ = []
|
||||
|
||||
|
||||
def public(f):
|
||||
__all__.append(f.__name__)
|
||||
return f
|
||||
|
||||
|
||||
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),
|
||||
ProcessorArchitecture="x64" if IS_X64 else "x86",
|
||||
)
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
REGISTRY = {
|
||||
"HKCU\\Software\\Python\\PythonCore": {
|
||||
VER_DOT: {
|
||||
"DisplayName": APPX_DATA["DisplayName"],
|
||||
"SupportUrl": "https://www.python.org/",
|
||||
"SysArchitecture": "64bit" if IS_X64 else "32bit",
|
||||
"SysVersion": VER_DOT,
|
||||
"Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO),
|
||||
"InstallPath": {
|
||||
# I have no idea why the trailing spaces are needed, but they seem to be needed.
|
||||
"": "[{AppVPackageRoot}][ ]",
|
||||
"ExecutablePath": "[{AppVPackageRoot}]python.exe[ ]",
|
||||
"WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[ ]",
|
||||
},
|
||||
"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
|
||||
|
||||
|
||||
@public
|
||||
def get_appx_layout(ns):
|
||||
if not ns.include_appxmanifest:
|
||||
return
|
||||
|
||||
yield "AppxManifest.xml", ns.temp / "AppxManifest.xml"
|
||||
yield "_resources.xml", ns.temp / "_resources.xml"
|
||||
icons = ns.source / "PC" / "icons"
|
||||
yield "_resources/pythonx44.png", icons / "pythonx44.png"
|
||||
yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png"
|
||||
yield "_resources/pythonx50.png", icons / "pythonx50.png"
|
||||
yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png"
|
||||
yield "_resources/pythonx150.png", icons / "pythonx150.png"
|
||||
yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png"
|
||||
yield "_resources/pythonwx44.png", icons / "pythonwx44.png"
|
||||
yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png"
|
||||
yield "_resources/pythonwx150.png", icons / "pythonwx150.png"
|
||||
yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.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
|
||||
|
||||
|
||||
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"'):
|
||||
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)
|
||||
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
|
||||
elif 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"))
|
||||
|
||||
|
||||
@public
|
||||
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
|
||||
|
||||
node = xml.find("m:Identity", NS)
|
||||
for k in node.keys():
|
||||
value = APPX_DATA.get(k)
|
||||
if value:
|
||||
node.set(k, value)
|
||||
|
||||
for node in xml.find("m:Properties", NS):
|
||||
value = APPX_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",
|
||||
["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)],
|
||||
PYTHON_VE_DATA,
|
||||
"console",
|
||||
("python.file", [".py"]),
|
||||
)
|
||||
|
||||
add_application(
|
||||
ns,
|
||||
xml,
|
||||
"PythonW",
|
||||
"pythonw",
|
||||
["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)],
|
||||
PYTHONW_VE_DATA,
|
||||
"windows",
|
||||
("python.windowedfile", [".pyw"]),
|
||||
)
|
||||
|
||||
if ns.include_pip and ns.include_launchers:
|
||||
add_application(
|
||||
ns,
|
||||
xml,
|
||||
"Pip",
|
||||
"pip",
|
||||
["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)],
|
||||
PIP_VE_DATA,
|
||||
"console",
|
||||
("python.wheel", [".whl"], 'install "%1"'),
|
||||
)
|
||||
|
||||
if ns.include_idle and ns.include_launchers:
|
||||
add_application(
|
||||
ns,
|
||||
xml,
|
||||
"Idle",
|
||||
"idle",
|
||||
["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()
|
||||
|
||||
|
||||
@public
|
||||
def get_resources_xml(ns):
|
||||
return RESOURCES_XML_TEMPLATE.encode("utf-8")
|
44
PC/layout/support/catalog.py
Normal file
44
PC/layout/support/catalog.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
"""
|
||||
File generation for catalog signing non-binary contents.
|
||||
"""
|
||||
|
||||
__author__ = "Steve Dower <steve.dower@python.org>"
|
||||
__version__ = "3.8"
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"]
|
||||
|
||||
|
||||
def public(f):
|
||||
__all__.append(f.__name__)
|
||||
return f
|
||||
|
||||
|
||||
PYTHON_CAT_NAME = "python.cat"
|
||||
PYTHON_CDF_NAME = "python.cdf"
|
||||
|
||||
|
||||
CATALOG_TEMPLATE = r"""[CatalogHeader]
|
||||
Name={target.stem}.cat
|
||||
ResultDir={target.parent}
|
||||
PublicVersion=1
|
||||
CatalogVersion=2
|
||||
HashAlgorithms=SHA256
|
||||
PageHashes=false
|
||||
EncodingType=
|
||||
|
||||
[CatalogFiles]
|
||||
"""
|
||||
|
||||
|
||||
def can_sign(file):
|
||||
return file.is_file() and file.stat().st_size
|
||||
|
||||
|
||||
@public
|
||||
def write_catalog(target, files):
|
||||
with target.open("w", encoding="utf-8") as cat:
|
||||
cat.write(CATALOG_TEMPLATE.format(target=target))
|
||||
cat.writelines("<HASH>{}={}\n".format(n, f) for n, f in files if can_sign(f))
|
28
PC/layout/support/constants.py
Normal file
28
PC/layout/support/constants.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
"""
|
||||
Constants for generating the layout.
|
||||
"""
|
||||
|
||||
__author__ = "Steve Dower <steve.dower@python.org>"
|
||||
__version__ = "3.8"
|
||||
|
||||
import struct
|
||||
import sys
|
||||
|
||||
VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion)
|
||||
VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4
|
||||
VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get(
|
||||
sys.version_info.releaselevel, ""
|
||||
)
|
||||
VER_SERIAL = sys.version_info.serial if VER_NAME else ""
|
||||
VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR)
|
||||
|
||||
PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR)
|
||||
PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR)
|
||||
PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR)
|
||||
PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR)
|
||||
|
||||
PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format(
|
||||
VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL
|
||||
)
|
||||
|
||||
IS_X64 = sys.maxsize > 2 ** 32
|
25
PC/layout/support/distutils.command.bdist_wininst.py
Normal file
25
PC/layout/support/distutils.command.bdist_wininst.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
"""distutils.command.bdist_wininst
|
||||
|
||||
Suppress the 'bdist_wininst' command, while still allowing
|
||||
setuptools to import it without breaking."""
|
||||
|
||||
from distutils.core import Command
|
||||
from distutils.errors import DistutilsPlatformError
|
||||
|
||||
|
||||
class bdist_wininst(Command):
|
||||
description = "create an executable installer for MS Windows"
|
||||
|
||||
# Marker for tests that we have the unsupported bdist_wininst
|
||||
_unsupported = True
|
||||
|
||||
def initialize_options(self):
|
||||
pass
|
||||
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
raise DistutilsPlatformError(
|
||||
"bdist_wininst is not supported in this Python distribution"
|
||||
)
|
100
PC/layout/support/filesets.py
Normal file
100
PC/layout/support/filesets.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
"""
|
||||
File sets and globbing helper for make_layout.
|
||||
"""
|
||||
|
||||
__author__ = "Steve Dower <steve.dower@python.org>"
|
||||
__version__ = "3.8"
|
||||
|
||||
import os
|
||||
|
||||
|
||||
class FileStemSet:
|
||||
def __init__(self, *patterns):
|
||||
self._names = set()
|
||||
self._prefixes = []
|
||||
self._suffixes = []
|
||||
for p in map(os.path.normcase, patterns):
|
||||
if p.endswith("*"):
|
||||
self._prefixes.append(p[:-1])
|
||||
elif p.startswith("*"):
|
||||
self._suffixes.append(p[1:])
|
||||
else:
|
||||
self._names.add(p)
|
||||
|
||||
def _make_name(self, f):
|
||||
return os.path.normcase(f.stem)
|
||||
|
||||
def __contains__(self, f):
|
||||
bn = self._make_name(f)
|
||||
return (
|
||||
bn in self._names
|
||||
or any(map(bn.startswith, self._prefixes))
|
||||
or any(map(bn.endswith, self._suffixes))
|
||||
)
|
||||
|
||||
|
||||
class FileNameSet(FileStemSet):
|
||||
def _make_name(self, f):
|
||||
return os.path.normcase(f.name)
|
||||
|
||||
|
||||
class FileSuffixSet:
|
||||
def __init__(self, *patterns):
|
||||
self._names = set()
|
||||
self._prefixes = []
|
||||
self._suffixes = []
|
||||
for p in map(os.path.normcase, patterns):
|
||||
if p.startswith("*."):
|
||||
self._names.add(p[1:])
|
||||
elif p.startswith("*"):
|
||||
self._suffixes.append(p[1:])
|
||||
elif p.endswith("*"):
|
||||
self._prefixes.append(p[:-1])
|
||||
elif p.startswith("."):
|
||||
self._names.add(p)
|
||||
else:
|
||||
self._names.add("." + p)
|
||||
|
||||
def _make_name(self, f):
|
||||
return os.path.normcase(f.suffix)
|
||||
|
||||
def __contains__(self, f):
|
||||
bn = self._make_name(f)
|
||||
return (
|
||||
bn in self._names
|
||||
or any(map(bn.startswith, self._prefixes))
|
||||
or any(map(bn.endswith, self._suffixes))
|
||||
)
|
||||
|
||||
|
||||
def _rglob(root, pattern, condition):
|
||||
dirs = [root]
|
||||
recurse = pattern[:3] in {"**/", "**\\"}
|
||||
if recurse:
|
||||
pattern = pattern[3:]
|
||||
|
||||
while dirs:
|
||||
d = dirs.pop(0)
|
||||
if recurse:
|
||||
dirs.extend(
|
||||
filter(
|
||||
condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir())
|
||||
)
|
||||
)
|
||||
yield from (
|
||||
(f.relative_to(root), f)
|
||||
for f in d.glob(pattern)
|
||||
if f.is_file() and condition(f)
|
||||
)
|
||||
|
||||
|
||||
def _return_true(f):
|
||||
return True
|
||||
|
||||
|
||||
def rglob(root, patterns, condition=None):
|
||||
if isinstance(patterns, tuple):
|
||||
for p in patterns:
|
||||
yield from _rglob(root, p, condition or _return_true)
|
||||
else:
|
||||
yield from _rglob(root, patterns, condition or _return_true)
|
93
PC/layout/support/logging.py
Normal file
93
PC/layout/support/logging.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
"""
|
||||
Logging support for make_layout.
|
||||
"""
|
||||
|
||||
__author__ = "Steve Dower <steve.dower@python.org>"
|
||||
__version__ = "3.8"
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
__all__ = []
|
||||
|
||||
LOG = None
|
||||
HAS_ERROR = False
|
||||
|
||||
|
||||
def public(f):
|
||||
__all__.append(f.__name__)
|
||||
return f
|
||||
|
||||
|
||||
@public
|
||||
def configure_logger(ns):
|
||||
global LOG
|
||||
if LOG:
|
||||
return
|
||||
|
||||
LOG = logging.getLogger("make_layout")
|
||||
LOG.level = logging.DEBUG
|
||||
|
||||
if ns.v:
|
||||
s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG)
|
||||
f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG)
|
||||
else:
|
||||
s_level = logging.ERROR
|
||||
f_level = logging.INFO
|
||||
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{"))
|
||||
handler.setLevel(s_level)
|
||||
LOG.addHandler(handler)
|
||||
|
||||
if ns.log:
|
||||
handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True)
|
||||
handler.setFormatter(
|
||||
logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{")
|
||||
)
|
||||
handler.setLevel(f_level)
|
||||
LOG.addHandler(handler)
|
||||
|
||||
|
||||
class BraceMessage:
|
||||
def __init__(self, fmt, *args, **kwargs):
|
||||
self.fmt = fmt
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __str__(self):
|
||||
return self.fmt.format(*self.args, **self.kwargs)
|
||||
|
||||
|
||||
@public
|
||||
def log_debug(msg, *args, **kwargs):
|
||||
return LOG.debug(BraceMessage(msg, *args, **kwargs))
|
||||
|
||||
|
||||
@public
|
||||
def log_info(msg, *args, **kwargs):
|
||||
return LOG.info(BraceMessage(msg, *args, **kwargs))
|
||||
|
||||
|
||||
@public
|
||||
def log_warning(msg, *args, **kwargs):
|
||||
return LOG.warning(BraceMessage(msg, *args, **kwargs))
|
||||
|
||||
|
||||
@public
|
||||
def log_error(msg, *args, **kwargs):
|
||||
global HAS_ERROR
|
||||
HAS_ERROR = True
|
||||
return LOG.error(BraceMessage(msg, *args, **kwargs))
|
||||
|
||||
|
||||
@public
|
||||
def log_exception(msg, *args, **kwargs):
|
||||
global HAS_ERROR
|
||||
HAS_ERROR = True
|
||||
return LOG.exception(BraceMessage(msg, *args, **kwargs))
|
||||
|
||||
|
||||
@public
|
||||
def error_was_logged():
|
||||
return HAS_ERROR
|
122
PC/layout/support/options.py
Normal file
122
PC/layout/support/options.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
"""
|
||||
List of optional components.
|
||||
"""
|
||||
|
||||
__author__ = "Steve Dower <steve.dower@python.org>"
|
||||
__version__ = "3.8"
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def public(f):
|
||||
__all__.append(f.__name__)
|
||||
return f
|
||||
|
||||
|
||||
OPTIONS = {
|
||||
"stable": {"help": "stable ABI stub"},
|
||||
"pip": {"help": "pip"},
|
||||
"distutils": {"help": "distutils"},
|
||||
"tcltk": {"help": "Tcl, Tk and tkinter"},
|
||||
"idle": {"help": "Idle"},
|
||||
"tests": {"help": "test suite"},
|
||||
"tools": {"help": "tools"},
|
||||
"venv": {"help": "venv"},
|
||||
"dev": {"help": "headers and libs"},
|
||||
"symbols": {"help": "symbols"},
|
||||
"bdist-wininst": {"help": "bdist_wininst support"},
|
||||
"underpth": {"help": "a python._pth file", "not-in-all": True},
|
||||
"launchers": {"help": "specific launchers"},
|
||||
"appxmanifest": {"help": "an appxmanifest"},
|
||||
"props": {"help": "a python.props file"},
|
||||
"chm": {"help": "the CHM documentation"},
|
||||
"html-doc": {"help": "the HTML documentation"},
|
||||
}
|
||||
|
||||
|
||||
PRESETS = {
|
||||
"appx": {
|
||||
"help": "APPX package",
|
||||
"options": [
|
||||
"stable",
|
||||
"pip",
|
||||
"distutils",
|
||||
"tcltk",
|
||||
"idle",
|
||||
"venv",
|
||||
"dev",
|
||||
"launchers",
|
||||
"appxmanifest",
|
||||
# XXX: Disabled for now "precompile",
|
||||
],
|
||||
},
|
||||
"nuget": {
|
||||
"help": "nuget package",
|
||||
"options": ["stable", "pip", "distutils", "dev", "props"],
|
||||
},
|
||||
"default": {
|
||||
"help": "development kit package",
|
||||
"options": [
|
||||
"stable",
|
||||
"pip",
|
||||
"distutils",
|
||||
"tcltk",
|
||||
"idle",
|
||||
"tests",
|
||||
"tools",
|
||||
"venv",
|
||||
"dev",
|
||||
"symbols",
|
||||
"bdist-wininst",
|
||||
"chm",
|
||||
],
|
||||
},
|
||||
"embed": {
|
||||
"help": "embeddable package",
|
||||
"options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@public
|
||||
def get_argparse_options():
|
||||
for opt, info in OPTIONS.items():
|
||||
help = "When specified, includes {}".format(info["help"])
|
||||
if info.get("not-in-all"):
|
||||
help = "{}. Not affected by --include-all".format(help)
|
||||
|
||||
yield "--include-{}".format(opt), help
|
||||
|
||||
for opt, info in PRESETS.items():
|
||||
help = "When specified, includes default options for {}".format(info["help"])
|
||||
yield "--preset-{}".format(opt), help
|
||||
|
||||
|
||||
def ns_get(ns, key, default=False):
|
||||
return getattr(ns, key.replace("-", "_"), default)
|
||||
|
||||
|
||||
def ns_set(ns, key, value=True):
|
||||
k1 = key.replace("-", "_")
|
||||
k2 = "include_{}".format(k1)
|
||||
if hasattr(ns, k2):
|
||||
setattr(ns, k2, value)
|
||||
elif hasattr(ns, k1):
|
||||
setattr(ns, k1, value)
|
||||
else:
|
||||
raise AttributeError("no argument named '{}'".format(k1))
|
||||
|
||||
|
||||
@public
|
||||
def update_presets(ns):
|
||||
for preset, info in PRESETS.items():
|
||||
if ns_get(ns, "preset-{}".format(preset)):
|
||||
for opt in info["options"]:
|
||||
ns_set(ns, opt)
|
||||
|
||||
if ns.include_all:
|
||||
for opt in OPTIONS:
|
||||
if OPTIONS[opt].get("not-in-all"):
|
||||
continue
|
||||
ns_set(ns, opt)
|
79
PC/layout/support/pip.py
Normal file
79
PC/layout/support/pip.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
"""
|
||||
Extraction and file list generation for pip.
|
||||
"""
|
||||
|
||||
__author__ = "Steve Dower <steve.dower@python.org>"
|
||||
__version__ = "3.8"
|
||||
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def public(f):
|
||||
__all__.append(f.__name__)
|
||||
return f
|
||||
|
||||
|
||||
@public
|
||||
def get_pip_dir(ns):
|
||||
if ns.copy:
|
||||
if ns.zip_lib:
|
||||
return ns.copy / "packages"
|
||||
return ns.copy / "Lib" / "site-packages"
|
||||
else:
|
||||
return ns.temp / "packages"
|
||||
|
||||
|
||||
@public
|
||||
def extract_pip_files(ns):
|
||||
dest = get_pip_dir(ns)
|
||||
dest.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
src = ns.source / "Lib" / "ensurepip" / "_bundled"
|
||||
|
||||
ns.temp.mkdir(parents=True, exist_ok=True)
|
||||
wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")]
|
||||
search_path = os.pathsep.join(wheels)
|
||||
if os.environ.get("PYTHONPATH"):
|
||||
search_path += ";" + os.environ["PYTHONPATH"]
|
||||
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = search_path
|
||||
|
||||
output = subprocess.check_output(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pip",
|
||||
"--no-color",
|
||||
"install",
|
||||
"pip",
|
||||
"setuptools",
|
||||
"--upgrade",
|
||||
"--target",
|
||||
str(dest),
|
||||
"--no-index",
|
||||
"--no-cache-dir",
|
||||
"-f",
|
||||
str(src),
|
||||
"--only-binary",
|
||||
":all:",
|
||||
],
|
||||
env=env,
|
||||
)
|
||||
|
||||
try:
|
||||
shutil.rmtree(dest / "bin")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
for file in wheels:
|
||||
try:
|
||||
os.remove(file)
|
||||
except OSError:
|
||||
pass
|
110
PC/layout/support/props.py
Normal file
110
PC/layout/support/props.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
"""
|
||||
Provides .props file.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from .constants import *
|
||||
|
||||
__all__ = ["PYTHON_PROPS_NAME"]
|
||||
|
||||
|
||||
def public(f):
|
||||
__all__.append(f.__name__)
|
||||
return f
|
||||
|
||||
|
||||
PYTHON_PROPS_NAME = "python.props"
|
||||
|
||||
PROPS_DATA = {
|
||||
"PYTHON_TAG": VER_DOT,
|
||||
"PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"),
|
||||
"PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"),
|
||||
"PYTHON_TARGET": "",
|
||||
}
|
||||
|
||||
if not PROPS_DATA["PYTHON_VERSION"]:
|
||||
if VER_NAME:
|
||||
PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format(
|
||||
VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL
|
||||
)
|
||||
else:
|
||||
PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO)
|
||||
|
||||
if not PROPS_DATA["PYTHON_PLATFORM"]:
|
||||
PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32"
|
||||
|
||||
PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format(
|
||||
VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"]
|
||||
)
|
||||
|
||||
PROPS_TEMPLATE = r"""<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="$(Platform) == '{PYTHON_PLATFORM}'">
|
||||
<PythonHome Condition="$(Configuration) == 'Debug'">$([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe")</PythonHome>
|
||||
<PythonHome Condition="$(PythonHome) == ''">$([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe")</PythonHome>
|
||||
<PythonInclude>$(PythonHome)\include</PythonInclude>
|
||||
<PythonLibs>$(PythonHome)\libs</PythonLibs>
|
||||
<PythonTag>{PYTHON_TAG}</PythonTag>
|
||||
<PythonVersion>{PYTHON_VERSION}</PythonVersion>
|
||||
|
||||
<IncludePythonExe Condition="$(IncludePythonExe) == ''">true</IncludePythonExe>
|
||||
<IncludeDistutils Condition="$(IncludeDistutils) == ''">false</IncludeDistutils>
|
||||
<IncludeLib2To3 Condition="$(IncludeLib2To3) == ''">false</IncludeLib2To3>
|
||||
<IncludeVEnv Condition="$(IncludeVEnv) == ''">false</IncludeVEnv>
|
||||
|
||||
<GetPythonRuntimeFilesDependsOn>{PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn)</GetPythonRuntimeFilesDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="$(Platform) == '{PYTHON_PLATFORM}'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(PythonInclude);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(PythonLibs);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Target Name="GetPythonRuntimeFiles" Returns="@(PythonRuntime)" DependsOnTargets="$(GetPythonRuntimeFilesDependsOn)" />
|
||||
|
||||
<Target Name="{PYTHON_TARGET}" Returns="@(PythonRuntime)">
|
||||
<ItemGroup>
|
||||
<_PythonRuntimeExe Include="$(PythonHome)\python*.dll" />
|
||||
<_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" />
|
||||
<_PythonRuntimeExe>
|
||||
<Link>%(Filename)%(Extension)</Link>
|
||||
</_PythonRuntimeExe>
|
||||
<_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" />
|
||||
<_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" />
|
||||
<_PythonRuntimeDlls>
|
||||
<Link>DLLs\%(Filename)%(Extension)</Link>
|
||||
</_PythonRuntimeDlls>
|
||||
<_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" />
|
||||
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" />
|
||||
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" />
|
||||
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" />
|
||||
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" />
|
||||
<_PythonRuntimeLib>
|
||||
<Link>Lib\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</_PythonRuntimeLib>
|
||||
<PythonRuntime Include="@(_PythonRuntimeExe);@(_PythonRuntimeDlls);@(_PythonRuntimeLib)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Message Importance="low" Text="Collected Python runtime from $(PythonHome):%0D%0A@(PythonRuntime->' %(Link)','%0D%0A')" />
|
||||
</Target>
|
||||
</Project>
|
||||
"""
|
||||
|
||||
|
||||
@public
|
||||
def get_props_layout(ns):
|
||||
if ns.include_all or ns.include_props:
|
||||
yield "python.props", ns.temp / "python.props"
|
||||
|
||||
|
||||
@public
|
||||
def get_props(ns):
|
||||
# TODO: Filter contents of props file according to included/excluded items
|
||||
props = PROPS_TEMPLATE.format_map(PROPS_DATA)
|
||||
return props.encode("utf-8")
|
56
PC/layout/support/python.props
Normal file
56
PC/layout/support/python.props
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="$(Platform) == '$$PYTHON_PLATFORM$$'">
|
||||
<PythonHome>$(MSBuildThisFileDirectory)\..\..\tools</PythonHome>
|
||||
<PythonInclude>$(PythonHome)\include</PythonInclude>
|
||||
<PythonLibs>$(PythonHome)\libs</PythonLibs>
|
||||
<PythonTag>$$PYTHON_TAG$$</PythonTag>
|
||||
<PythonVersion>$$PYTHON_VERSION$$</PythonVersion>
|
||||
|
||||
<IncludePythonExe Condition="$(IncludePythonExe) == ''">true</IncludePythonExe>
|
||||
<IncludeDistutils Condition="$(IncludeDistutils) == ''">false</IncludeDistutils>
|
||||
<IncludeLib2To3 Condition="$(IncludeLib2To3) == ''">false</IncludeLib2To3>
|
||||
<IncludeVEnv Condition="$(IncludeVEnv) == ''">false</IncludeVEnv>
|
||||
|
||||
<GetPythonRuntimeFilesDependsOn>$$PYTHON_TARGET$$;$(GetPythonRuntimeFilesDependsOn)</GetPythonRuntimeFilesDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup Condition="$(Platform) == '$$PYTHON_PLATFORM$$'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(PythonInclude);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(PythonLibs);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Target Name="GetPythonRuntimeFiles" Returns="@(PythonRuntime)" DependsOnTargets="$(GetPythonRuntimeFilesDependsOn)" />
|
||||
|
||||
<Target Name="$$PYTHON_TARGET$$" Returns="@(PythonRuntime)">
|
||||
<ItemGroup>
|
||||
<_PythonRuntimeExe Include="$(PythonHome)\python*.dll" />
|
||||
<_PythonRuntimeExe Include="$(PythonHome)\vcruntime140.dll" />
|
||||
<_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" />
|
||||
<_PythonRuntimeExe>
|
||||
<Link>%(Filename)%(Extension)</Link>
|
||||
</_PythonRuntimeExe>
|
||||
<_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" />
|
||||
<_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" />
|
||||
<_PythonRuntimeDlls>
|
||||
<Link>DLLs\%(Filename)%(Extension)</Link>
|
||||
</_PythonRuntimeDlls>
|
||||
<_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" />
|
||||
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" />
|
||||
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" />
|
||||
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" />
|
||||
<_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" />
|
||||
<_PythonRuntimeLib>
|
||||
<Link>Lib\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</_PythonRuntimeLib>
|
||||
<PythonRuntime Include="@(_PythonRuntimeExe);@(_PythonRuntimeDlls);@(_PythonRuntimeLib)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Message Importance="low" Text="Collected Python runtime from $(PythonHome):%0D%0A@(PythonRuntime->' %(Link)','%0D%0A')" />
|
||||
</Target>
|
||||
</Project>
|
Loading…
Add table
Add a link
Reference in a new issue