112 lines
3.3 KiB
Python
112 lines
3.3 KiB
Python
"""PyInstaller freeze helpers: pin CPython Tcl/Tk, drop Graphviz's copy."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def _python_dll_dir() -> Path | None:
|
|
candidates = [
|
|
Path(sys.base_prefix) / "DLLs",
|
|
Path(sys.prefix) / "DLLs",
|
|
Path(sys.executable).resolve().parent / "DLLs",
|
|
Path(sys.executable).resolve().parent.parent / "DLLs",
|
|
]
|
|
for folder in candidates:
|
|
if (folder / "tcl86t.dll").is_file() or (folder / "tk86t.dll").is_file():
|
|
return folder
|
|
return None
|
|
|
|
|
|
def prepare_tcltk_env() -> None:
|
|
"""Make Analysis / isolated hook subprocesses see this interpreter's Tcl."""
|
|
dll_dir = _python_dll_dir()
|
|
prefix = Path(sys.base_prefix)
|
|
tcl_lib = prefix / "tcl" / "tcl8.6"
|
|
tk_lib = prefix / "tcl" / "tk8.6"
|
|
if not tcl_lib.is_dir() and dll_dir is not None:
|
|
tcl_lib = dll_dir.parent / "tcl" / "tcl8.6"
|
|
tk_lib = dll_dir.parent / "tcl" / "tk8.6"
|
|
if tcl_lib.is_dir():
|
|
os.environ["TCL_LIBRARY"] = str(tcl_lib)
|
|
if tk_lib.is_dir():
|
|
os.environ["TK_LIBRARY"] = str(tk_lib)
|
|
if dll_dir is not None:
|
|
os.environ["PATH"] = str(dll_dir) + os.pathsep + os.environ.get("PATH", "")
|
|
|
|
|
|
def collect_tkinterdnd2():
|
|
datas: list = []
|
|
hidden: list = ["tkinterdnd2", "tkinterdnd2.TkinterDnD"]
|
|
try:
|
|
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
|
|
|
datas += collect_data_files("tkinterdnd2")
|
|
hidden = list(collect_submodules("tkinterdnd2")) or hidden
|
|
except Exception:
|
|
pass
|
|
return datas, hidden
|
|
|
|
|
|
def _toc_name(item) -> str:
|
|
dest = str(item[0]).replace("\\", "/")
|
|
return os.path.basename(dest).lower()
|
|
|
|
|
|
def _toc_blob(item) -> str:
|
|
dest = str(item[0]).replace("\\", "/").lower()
|
|
src = str(item[1]).replace("\\", "/").lower() if len(item) > 1 else ""
|
|
return dest + " " + src
|
|
|
|
|
|
def _graphviz_tcl_names() -> set[str]:
|
|
try:
|
|
from md2gost.dfd import GRAPHVIZ_TCL_DLL_NAMES
|
|
|
|
return {n.lower() for n in GRAPHVIZ_TCL_DLL_NAMES}
|
|
except Exception:
|
|
return {
|
|
"tcl86t.dll",
|
|
"tk86t.dll",
|
|
"tcl86.dll",
|
|
"tk86.dll",
|
|
"tcldot.dll",
|
|
"tcldot_builtin.dll",
|
|
"tclplan.dll",
|
|
"gdtclft.dll",
|
|
}
|
|
|
|
|
|
def _is_graphviz_tcl(item) -> bool:
|
|
if _toc_name(item) not in _graphviz_tcl_names():
|
|
return False
|
|
return "graphviz" in _toc_blob(item)
|
|
|
|
|
|
def _wrap_toc(items):
|
|
try:
|
|
from PyInstaller.building.datastruct import TOC
|
|
|
|
return TOC(items)
|
|
except Exception:
|
|
return items
|
|
|
|
|
|
def sanitize_analysis(a):
|
|
"""Drop Graphviz Tcl/Tk DLLs and force this CPython's tcl86t/tk86t into the exe."""
|
|
a.datas = _wrap_toc([x for x in a.datas if not _is_graphviz_tcl(x)])
|
|
binaries = [x for x in a.binaries if not _is_graphviz_tcl(x)]
|
|
dll_dir = _python_dll_dir()
|
|
pin = ("tcl86t.dll", "tk86t.dll", "zlib1.dll")
|
|
if dll_dir is not None:
|
|
pin_lower = {n.lower() for n in pin}
|
|
binaries = [x for x in binaries if _toc_name(x) not in pin_lower]
|
|
for name in pin:
|
|
path = dll_dir / name
|
|
if path.is_file():
|
|
binaries.append((name, str(path), "BINARY"))
|
|
a.binaries = _wrap_toc(binaries)
|
|
return a
|