mit neuen venv und exe-Files
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
94
venv3_12/Lib/site-packages/cx_Freeze/command/_pydialog.py
Normal file
94
venv3_12/Lib/site-packages/cx_Freeze/command/_pydialog.py
Normal file
@@ -0,0 +1,94 @@
|
||||
"""Extend msilib Dialog."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from msilib import Control, Dialog
|
||||
|
||||
|
||||
class PyDialog(Dialog):
|
||||
"""Dialog class with a fixed layout: controls at the top, then a ruler,
|
||||
then a list of buttons: back, next, cancel. Optionally a bitmap at the
|
||||
left.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
db,
|
||||
name,
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
attr,
|
||||
title,
|
||||
first,
|
||||
default,
|
||||
cancel,
|
||||
bitmap=True, # noqa: ARG002
|
||||
) -> None:
|
||||
Dialog.__init__(
|
||||
self, db, name, x, y, w, h, attr, title, first, default, cancel
|
||||
)
|
||||
ruler = self.h - 36
|
||||
# bmwidth = 152 * ruler / 328
|
||||
# if kw.get("bitmap", True):
|
||||
# self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
|
||||
self.line("BottomLine", 0, ruler, self.w, 0)
|
||||
|
||||
def title(self, title) -> None:
|
||||
"""Set the title text of the dialog at the top."""
|
||||
# flags=0x30003=Visible|Enabled|Transparent|NoPrefix
|
||||
# text, in VerdanaBold10
|
||||
font = r"{\VerdanaBold10}"
|
||||
self.text("Title", 15, 10, 320, 60, 0x30003, f"{font}{title}")
|
||||
|
||||
def backbutton(self, title, tabnext, name="Back", active=1) -> Control:
|
||||
"""Add a back button with a given title, the tab-next button,
|
||||
its name in the Control table, possibly initially disabled.
|
||||
|
||||
Return the button, so that events can be associated
|
||||
"""
|
||||
flags = 3 if active else 1 # Visible|Enabled or Visible
|
||||
return self.pushbutton(
|
||||
name, 180, self.h - 27, 56, 17, flags, title, tabnext
|
||||
)
|
||||
|
||||
def cancelbutton(self, title, tabnext, name="Cancel", active=1) -> Control:
|
||||
"""Add a cancel button with a given title, the tab-next button,
|
||||
its name in the Control table, possibly initially disabled.
|
||||
|
||||
Return the button, so that events can be associated
|
||||
"""
|
||||
flags = 3 if active else 1 # Visible|Enabled or Visible
|
||||
return self.pushbutton(
|
||||
name, 304, self.h - 27, 56, 17, flags, title, tabnext
|
||||
)
|
||||
|
||||
def nextbutton(self, title, tabnext, name="Next", active=1) -> Control:
|
||||
"""Add a Next button with a given title, the tab-next button,
|
||||
its name in the Control table, possibly initially disabled.
|
||||
|
||||
Return the button, so that events can be associated
|
||||
"""
|
||||
flags = 3 if active else 1 # Visible|Enabled or Visible
|
||||
return self.pushbutton(
|
||||
name, 236, self.h - 27, 56, 17, flags, title, tabnext
|
||||
)
|
||||
|
||||
def xbutton(self, name, title, tabnext, xpos) -> Control:
|
||||
"""Add a button with a given title, the tab-next button,
|
||||
its name in the Control table, giving its x position; the
|
||||
y-position is aligned with the other buttons.
|
||||
|
||||
Return the button, so that events can be associated
|
||||
"""
|
||||
return self.pushbutton(
|
||||
name,
|
||||
int(self.w * xpos - 28),
|
||||
self.h - 27,
|
||||
56,
|
||||
17,
|
||||
3,
|
||||
title,
|
||||
tabnext,
|
||||
)
|
||||
313
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_appimage.py
Normal file
313
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_appimage.py
Normal file
@@ -0,0 +1,313 @@
|
||||
"""Implements the 'bdist_appimage' command (create Linux AppImage format).
|
||||
|
||||
https://appimage.org/
|
||||
https://docs.appimage.org/
|
||||
https://docs.appimage.org/packaging-guide/manual.html#ref-manual
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import stat
|
||||
from ctypes.util import find_library
|
||||
from logging import INFO, WARNING
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
from typing import ClassVar
|
||||
from urllib.request import urlretrieve
|
||||
from zipfile import ZipFile
|
||||
|
||||
from filelock import FileLock
|
||||
from setuptools import Command
|
||||
|
||||
import cx_Freeze.icons
|
||||
from cx_Freeze.exception import ExecError, PlatformError
|
||||
|
||||
__all__ = ["bdist_appimage"]
|
||||
|
||||
ARCH = platform.machine()
|
||||
APPIMAGEKIT_URL = "https://github.com/AppImage/AppImageKit/releases"
|
||||
APPIMAGEKIT_PATH = f"download/continuous/appimagetool-{ARCH}.AppImage"
|
||||
APPIMAGEKIT_TOOL = "~/.local/bin/appimagetool"
|
||||
|
||||
|
||||
class bdist_appimage(Command):
|
||||
"""Create a Linux AppImage."""
|
||||
|
||||
description = "create a Linux AppImage"
|
||||
user_options: ClassVar[list[tuple[str, str | None, str]]] = [
|
||||
(
|
||||
"appimagekit=",
|
||||
None,
|
||||
f'path to AppImageKit [default: "{APPIMAGEKIT_TOOL}"]',
|
||||
),
|
||||
(
|
||||
"bdist-base=",
|
||||
None,
|
||||
"base directory for creating built distributions",
|
||||
),
|
||||
(
|
||||
"build-dir=",
|
||||
"b",
|
||||
"directory of built executables and dependent files",
|
||||
),
|
||||
(
|
||||
"dist-dir=",
|
||||
"d",
|
||||
'directory to put final built distributions in [default: "dist"]',
|
||||
),
|
||||
(
|
||||
"skip-build",
|
||||
None,
|
||||
"skip rebuilding everything (for testing/debugging)",
|
||||
),
|
||||
("target-name=", None, "name of the file to create"),
|
||||
("target-version=", None, "version of the file to create"),
|
||||
("silent", "s", "suppress all output except warnings"),
|
||||
]
|
||||
boolean_options: ClassVar[list[str]] = [
|
||||
"skip-build",
|
||||
"silent",
|
||||
]
|
||||
|
||||
def initialize_options(self) -> None:
|
||||
self.appimagekit = None
|
||||
|
||||
self.bdist_base = None
|
||||
self.build_dir = None
|
||||
self.dist_dir = None
|
||||
self.skip_build = None
|
||||
|
||||
self.target_name = None
|
||||
self.target_version = None
|
||||
self.fullname = None
|
||||
self.silent = None
|
||||
|
||||
self._warnings = []
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
if os.name != "posix":
|
||||
msg = (
|
||||
"don't know how to create AppImage "
|
||||
f"distributions on platform {os.name}"
|
||||
)
|
||||
raise PlatformError(msg)
|
||||
|
||||
# inherit options
|
||||
self.set_undefined_options(
|
||||
"build_exe",
|
||||
("build_exe", "build_dir"),
|
||||
("silent", "silent"),
|
||||
)
|
||||
self.set_undefined_options(
|
||||
"bdist",
|
||||
("bdist_base", "bdist_base"),
|
||||
("dist_dir", "dist_dir"),
|
||||
("skip_build", "skip_build"),
|
||||
)
|
||||
# for the bdist commands, there is a chance that build_exe has already
|
||||
# been executed, so check skip_build if build_exe have_run
|
||||
if not self.skip_build and self.distribution.have_run.get("build_exe"):
|
||||
self.skip_build = 1
|
||||
|
||||
if self.target_name is None:
|
||||
if self.distribution.metadata.name:
|
||||
self.target_name = self.distribution.metadata.name
|
||||
else:
|
||||
executables = self.distribution.executables
|
||||
executable = executables[0]
|
||||
self.warn_delayed(
|
||||
"using the first executable as target_name: "
|
||||
f"{executable.target_name}"
|
||||
)
|
||||
self.target_name = executable.target_name
|
||||
|
||||
if self.target_version is None and self.distribution.metadata.version:
|
||||
self.target_version = self.distribution.metadata.version
|
||||
|
||||
name = self.target_name
|
||||
version = self.target_version
|
||||
name, ext = os.path.splitext(name)
|
||||
if ext == ".AppImage":
|
||||
self.app_name = self.target_name
|
||||
self.fullname = name
|
||||
elif version:
|
||||
self.app_name = f"{name}-{version}-{ARCH}.AppImage"
|
||||
self.fullname = f"{name}-{version}"
|
||||
else:
|
||||
self.app_name = f"{name}-{ARCH}.AppImage"
|
||||
self.fullname = name
|
||||
|
||||
if self.silent is not None:
|
||||
self.verbose = 0 if self.silent else 2
|
||||
build_exe = self.distribution.command_obj.get("build_exe")
|
||||
if build_exe:
|
||||
build_exe.silent = self.silent
|
||||
|
||||
# validate or download appimagekit
|
||||
self._get_appimagekit()
|
||||
|
||||
def _get_appimagekit(self) -> None:
|
||||
"""Fetch AppImageKit from the web if not available locally."""
|
||||
appimagekit = os.path.expanduser(self.appimagekit or APPIMAGEKIT_TOOL)
|
||||
appimagekit_dir = os.path.dirname(appimagekit)
|
||||
self.mkpath(appimagekit_dir)
|
||||
with FileLock(appimagekit + ".lock"):
|
||||
if not os.path.exists(appimagekit):
|
||||
self.announce(
|
||||
f"download and install AppImageKit from {APPIMAGEKIT_URL}",
|
||||
INFO,
|
||||
)
|
||||
name = os.path.basename(APPIMAGEKIT_PATH)
|
||||
filename = os.path.join(appimagekit_dir, name)
|
||||
if not os.path.exists(filename):
|
||||
urlretrieve( # noqa: S310
|
||||
os.path.join(APPIMAGEKIT_URL, APPIMAGEKIT_PATH),
|
||||
filename,
|
||||
)
|
||||
os.chmod(filename, stat.S_IRWXU)
|
||||
if not os.path.exists(appimagekit):
|
||||
self.execute(
|
||||
os.symlink,
|
||||
(filename, appimagekit),
|
||||
msg=f"linking {appimagekit} -> {filename}",
|
||||
)
|
||||
self.appimagekit = appimagekit
|
||||
|
||||
def run(self) -> None:
|
||||
# Create the application bundle
|
||||
if not self.skip_build:
|
||||
self.run_command("build_exe")
|
||||
|
||||
# Make appimage (by default in dist directory)
|
||||
# Set the full path of appimage to be built
|
||||
self.mkpath(self.dist_dir)
|
||||
output = os.path.abspath(os.path.join(self.dist_dir, self.app_name))
|
||||
if os.path.exists(output):
|
||||
os.unlink(output)
|
||||
|
||||
# Make AppDir folder
|
||||
appdir = os.path.join(self.bdist_base, "AppDir")
|
||||
if os.path.exists(appdir):
|
||||
self.execute(shutil.rmtree, (appdir,), msg=f"removing {appdir}")
|
||||
self.mkpath(appdir)
|
||||
|
||||
# Copy from build_exe
|
||||
self.copy_tree(self.build_dir, appdir, preserve_symlinks=True)
|
||||
|
||||
# Remove zip file after putting all files in the file system
|
||||
# (appimage is a compressed file, no need of internal zip file)
|
||||
library_data = Path(appdir, "lib", "library.dat")
|
||||
if library_data.exists():
|
||||
target_lib_dir = library_data.parent
|
||||
filename = target_lib_dir / library_data.read_bytes().decode()
|
||||
with ZipFile(filename) as outfile:
|
||||
outfile.extractall(target_lib_dir)
|
||||
filename.unlink()
|
||||
library_data.unlink()
|
||||
|
||||
# Add icon, desktop file, entrypoint
|
||||
share_icons = os.path.join("share", "icons")
|
||||
icons_dir = os.path.join(appdir, share_icons)
|
||||
self.mkpath(icons_dir)
|
||||
|
||||
executables = self.distribution.executables
|
||||
executable = executables[0]
|
||||
if len(executables) > 1:
|
||||
self.warn_delayed(
|
||||
"using the first executable as entrypoint: "
|
||||
f"{executable.target_name}"
|
||||
)
|
||||
if executable.icon is None:
|
||||
icon_name = "logox128.png"
|
||||
icon_source_dir = os.path.dirname(cx_Freeze.icons.__file__)
|
||||
self.copy_file(os.path.join(icon_source_dir, icon_name), icons_dir)
|
||||
else:
|
||||
icon_name = executable.icon.name
|
||||
self.move_file(os.path.join(appdir, icon_name), icons_dir)
|
||||
relative_reference = os.path.join(share_icons, icon_name)
|
||||
origin = os.path.join(appdir, ".DirIcon")
|
||||
self.execute(
|
||||
os.symlink,
|
||||
(relative_reference, origin),
|
||||
msg=f"linking {origin} -> {relative_reference}",
|
||||
)
|
||||
|
||||
desktop_entry = f"""\
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name={self.target_name}
|
||||
Exec={executable.target_name}
|
||||
Comment={self.distribution.get_description()}
|
||||
Icon=/{share_icons}/{os.path.splitext(icon_name)[0]}
|
||||
Categories=Development;
|
||||
Terminal=true
|
||||
X-AppImage-Arch={ARCH}
|
||||
X-AppImage-Name={self.target_name}
|
||||
X-AppImage-Version={self.target_version or ''}
|
||||
"""
|
||||
self.save_as_file(
|
||||
dedent(desktop_entry),
|
||||
os.path.join(appdir, f"{self.target_name}.desktop"),
|
||||
)
|
||||
entrypoint = f"""\
|
||||
#! /bin/bash
|
||||
# If running from an extracted image, fix APPDIR
|
||||
if [ -z "$APPIMAGE" ]; then
|
||||
self="$(readlink -f -- $0)"
|
||||
export APPDIR="${{self%/*}}"
|
||||
fi
|
||||
# Call the application entry point
|
||||
"$APPDIR/{executable.target_name}" "$@"
|
||||
"""
|
||||
self.save_as_file(
|
||||
dedent(entrypoint), os.path.join(appdir, "AppRun"), mode="x"
|
||||
)
|
||||
|
||||
# Build an AppImage from an AppDir
|
||||
os.environ["ARCH"] = ARCH
|
||||
cmd = [self.appimagekit, "--no-appstream", appdir, output]
|
||||
if find_library("fuse") is None: # libfuse.so.2 is not found
|
||||
cmd.insert(1, "--appimage-extract-and-run")
|
||||
with FileLock(self.appimagekit + ".lock"):
|
||||
self.spawn(cmd, search_path=0)
|
||||
if not os.path.exists(output):
|
||||
msg = "Could not build AppImage"
|
||||
raise ExecError(msg)
|
||||
|
||||
self.warnings()
|
||||
|
||||
def save_as_file(self, data, outfile, mode="r") -> tuple[str, int]:
|
||||
"""Save an input data to a file respecting verbose, dry-run and force
|
||||
flags.
|
||||
"""
|
||||
if not self.force and os.path.exists(outfile):
|
||||
if self.verbose >= 1:
|
||||
self.warn_delayed(f"not creating {outfile} (output exists)")
|
||||
return (outfile, 0)
|
||||
if self.verbose >= 1:
|
||||
self.announce(f"creating {outfile}", INFO)
|
||||
|
||||
if self.dry_run:
|
||||
return (outfile, 1)
|
||||
|
||||
if isinstance(data, str):
|
||||
data = data.encode()
|
||||
with open(outfile, "wb") as out:
|
||||
out.write(data)
|
||||
st_mode = stat.S_IRUSR
|
||||
if "w" in mode:
|
||||
st_mode = st_mode | stat.S_IWUSR
|
||||
if "x" in mode:
|
||||
st_mode = st_mode | stat.S_IXUSR
|
||||
os.chmod(outfile, st_mode)
|
||||
return (outfile, 1)
|
||||
|
||||
def warn_delayed(self, msg) -> None:
|
||||
self._warnings.append(msg)
|
||||
|
||||
def warnings(self) -> None:
|
||||
for msg in self._warnings:
|
||||
self.announce(f"WARNING: {msg}", WARNING)
|
||||
122
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_deb.py
Normal file
122
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_deb.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""Implements the 'bdist_deb' command (create DEB binary distributions).
|
||||
|
||||
This is a simple wrapper around 'alien' that converts a rpm to deb.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import ClassVar
|
||||
|
||||
from setuptools import Command
|
||||
|
||||
from cx_Freeze.command.bdist_rpm import bdist_rpm
|
||||
from cx_Freeze.exception import ExecError, PlatformError
|
||||
|
||||
__all__ = ["bdist_deb"]
|
||||
|
||||
|
||||
class bdist_deb(Command):
|
||||
"""Create an DEB distribution."""
|
||||
|
||||
description = "create an DEB distribution"
|
||||
|
||||
user_options: ClassVar[list[tuple[str, str | None, str]]] = [
|
||||
(
|
||||
"bdist-base=",
|
||||
None,
|
||||
"base directory for creating built distributions",
|
||||
),
|
||||
(
|
||||
"build-dir=",
|
||||
"b",
|
||||
"directory of built executables and dependent files",
|
||||
),
|
||||
("dist-dir=", "d", "directory to put final built distributions in"),
|
||||
]
|
||||
|
||||
def initialize_options(self) -> None:
|
||||
self.bdist_base = None
|
||||
self.build_dir = None
|
||||
self.dist_dir = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
if os.name != "posix":
|
||||
msg = (
|
||||
"don't know how to create DEB "
|
||||
f"distributions on platform {os.name}"
|
||||
)
|
||||
raise PlatformError(msg)
|
||||
if not shutil.which("alien"):
|
||||
msg = "failed to find 'alien' for this platform."
|
||||
raise PlatformError(msg)
|
||||
if os.getuid() != 0 and not shutil.which("fakeroot"):
|
||||
msg = "failed to find 'fakeroot' for this platform."
|
||||
raise PlatformError(msg)
|
||||
|
||||
self.set_undefined_options("bdist", ("bdist_base", "bdist_base"))
|
||||
self.set_undefined_options(
|
||||
"bdist",
|
||||
("bdist_base", "bdist_base"),
|
||||
("dist_dir", "dist_dir"),
|
||||
)
|
||||
|
||||
def run(self) -> None:
|
||||
# make a binary RPM to convert
|
||||
cmd_rpm = bdist_rpm(
|
||||
self.distribution,
|
||||
bdist_base=self.bdist_base,
|
||||
dist_dir=self.dist_dir,
|
||||
)
|
||||
cmd_rpm.ensure_finalized()
|
||||
if not self.dry_run:
|
||||
cmd_rpm.run()
|
||||
rpm_filename = None
|
||||
for command, _, filename in self.distribution.dist_files:
|
||||
if command == "bdist_rpm":
|
||||
rpm_filename = os.path.basename(filename)
|
||||
break
|
||||
if rpm_filename is None:
|
||||
msg = "could not build rpm"
|
||||
raise ExecError(msg)
|
||||
else:
|
||||
rpm_filename = "filename.rpm"
|
||||
|
||||
# convert rpm to deb (by default in dist directory)
|
||||
logging.info("building DEB")
|
||||
cmd = ["alien", "--to-deb", rpm_filename]
|
||||
if os.getuid() != 0:
|
||||
cmd.insert(0, "fakeroot")
|
||||
if self.dry_run:
|
||||
self.spawn(cmd)
|
||||
else:
|
||||
logging.info(subprocess.list2cmdline(cmd))
|
||||
process = subprocess.run(
|
||||
cmd,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
cwd=self.dist_dir,
|
||||
)
|
||||
if process.returncode != 0:
|
||||
msg = process.stderr.splitlines()[0]
|
||||
if msg.startswith(f"Unpacking of '{rpm_filename}' failed at"):
|
||||
info = [
|
||||
"\n\t\x08Please check if you have `cpio 2.13` on "
|
||||
"Ubuntu 22.04.",
|
||||
"\t\x08You can try to install a previous version:",
|
||||
"\t\x08$ sudo apt-get install cpio=2.13+dfsg-7",
|
||||
]
|
||||
msg += "\n".join(info)
|
||||
raise ExecError(msg)
|
||||
output = process.stdout
|
||||
logging.info(output)
|
||||
filename = output.splitlines()[0].split()[0]
|
||||
filename = os.path.join(self.dist_dir, filename)
|
||||
if not os.path.exists(filename):
|
||||
msg = "could not build deb"
|
||||
raise ExecError(msg)
|
||||
self.distribution.dist_files.append(("bdist_deb", "any", filename))
|
||||
385
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_dmg.py
Normal file
385
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_dmg.py
Normal file
@@ -0,0 +1,385 @@
|
||||
"""Implements the 'bdist_dmg' command (create macOS dmg and/or app bundle)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from typing import ClassVar
|
||||
|
||||
from dmgbuild import build_dmg
|
||||
from setuptools import Command
|
||||
|
||||
import cx_Freeze.icons
|
||||
from cx_Freeze import Executable
|
||||
from cx_Freeze.exception import OptionError
|
||||
|
||||
__all__ = ["bdist_dmg"]
|
||||
|
||||
|
||||
class bdist_dmg(Command):
|
||||
"""Create a Mac DMG disk image containing the Mac application bundle."""
|
||||
|
||||
description = (
|
||||
"create a Mac DMG disk image containing the Mac application bundle"
|
||||
)
|
||||
user_options: ClassVar[list[tuple[str, str | None, str]]] = [
|
||||
("volume-label=", None, "Volume label of the DMG disk image"),
|
||||
(
|
||||
"applications-shortcut=",
|
||||
None,
|
||||
"Boolean for whether to include "
|
||||
"shortcut to Applications in the DMG disk image",
|
||||
),
|
||||
("silent", "s", "suppress all output except warnings"),
|
||||
("format=", None, 'format of the disk image [default: "UDZO"]'),
|
||||
(
|
||||
"filesystem=",
|
||||
None,
|
||||
'filesystem of the disk image [default: "HFS+"]',
|
||||
),
|
||||
(
|
||||
"size=",
|
||||
None,
|
||||
"If defined, specifies the size of the filesystem within the "
|
||||
"image. If this is not defined, cx_Freeze (and then dmgbuild) "
|
||||
"will attempt to determine a reasonable size for the image. "
|
||||
"If you set this, you should set it large enough to hold the "
|
||||
"files you intend to copy into the image. The syntax is the "
|
||||
"same as for the -size argument to hdiutil, i.e. you can use "
|
||||
"the suffixes `b`, `k`, `m`, `g`, `t`, `p` and `e` for bytes, "
|
||||
"kilobytes, megabytes, gigabytes, terabytes, exabytes and "
|
||||
"petabytes respectively.",
|
||||
),
|
||||
(
|
||||
"background",
|
||||
"b",
|
||||
"A rgb color in the form #3344ff, svg named color like goldenrod, "
|
||||
"a path to an image, or the words 'builtin-arrow' [default: None]",
|
||||
),
|
||||
(
|
||||
"show-status-bar",
|
||||
None,
|
||||
"Show the status bar in the Finder window. Default is False.",
|
||||
),
|
||||
(
|
||||
"show-tab-view",
|
||||
None,
|
||||
"Show the tab view in the Finder window. Default is False.",
|
||||
),
|
||||
(
|
||||
"show-path-bar",
|
||||
None,
|
||||
"Show the path bar in the Finder window. Default is False.",
|
||||
),
|
||||
(
|
||||
"show-sidebar",
|
||||
None,
|
||||
"Show the sidebar in the Finder window. Default is False.",
|
||||
),
|
||||
(
|
||||
"sidebar-width",
|
||||
None,
|
||||
"Width of the sidebar in the Finder window. Default is None.",
|
||||
),
|
||||
(
|
||||
"window-rect",
|
||||
None,
|
||||
"Window rectangle in the form x, y, width, height. The position "
|
||||
"of the window in ((x, y), (w, h)) format, with y co-ordinates "
|
||||
"running from bottom to top. The Finder makes sure that the "
|
||||
"window will be on the user's display, so if you want your window "
|
||||
"at the top left of the display you could use (0, 100000) as the "
|
||||
"x, y co-ordinates. Unfortunately it doesn't appear to be "
|
||||
"possible to position the window relative to the top left or "
|
||||
"relative to the centre of the user's screen.",
|
||||
),
|
||||
(
|
||||
"icon-locations",
|
||||
None,
|
||||
"A dictionary specifying the co-ordinates of items in the root "
|
||||
"directory of the disk image, where the keys are filenames and "
|
||||
"the values are (x, y) tuples. e.g.: "
|
||||
'icon-locations = { "Applications": (100, 100), '
|
||||
'"README.txt": (200, 100) }',
|
||||
),
|
||||
(
|
||||
"default-view",
|
||||
None,
|
||||
"The default view of the Finder window. Possible values are "
|
||||
'"icon-view", "list-view", "column-view", "coverflow".',
|
||||
),
|
||||
(
|
||||
"show-icon-preview",
|
||||
None,
|
||||
"Show icon preview in the Finder window. Default is False.",
|
||||
),
|
||||
(
|
||||
"license",
|
||||
None,
|
||||
"Dictionary specifying license details with 'default-language', "
|
||||
"'licenses', and 'buttons'."
|
||||
"default-language: Language code (e.g., 'en_US') if no matching "
|
||||
"system language."
|
||||
"licenses: Map of language codes to license file paths "
|
||||
"(e.g., {'en_US': 'path/to/license_en.txt'})."
|
||||
"buttons: Map of language codes to UI strings "
|
||||
"([language, agree, disagree, print, save, instruction])."
|
||||
"Example: {'default-language': 'en_US', "
|
||||
"'licenses': {'en_US': 'path/to/license_en.txt'}, "
|
||||
"'buttons': {'en_US': ['English', 'Agree', 'Disagree', 'Print', "
|
||||
"'Save', 'Instruction text']}}",
|
||||
),
|
||||
]
|
||||
|
||||
def initialize_options(self) -> None:
|
||||
self.silent = None
|
||||
self.volume_label = self.distribution.get_fullname()
|
||||
self.applications_shortcut = False
|
||||
self._symlinks = {}
|
||||
self._files = []
|
||||
self.format = "UDZO"
|
||||
self.filesystem = "HFS+"
|
||||
self.size = None
|
||||
self.background = None
|
||||
self.show_status_bar = False
|
||||
self.show_tab_view = False
|
||||
self.show_path_bar = False
|
||||
self.show_sidebar = False
|
||||
self.sidebar_width = None
|
||||
self.window_rect = None
|
||||
self.hide = None
|
||||
self.hide_extensions = None
|
||||
self.icon_locations = None
|
||||
self.default_view = None
|
||||
self.show_icon_preview = False
|
||||
self.license = None
|
||||
|
||||
# Non-exposed options
|
||||
self.include_icon_view_settings = "auto"
|
||||
self.include_list_view_settings = "auto"
|
||||
self.arrange_by = None
|
||||
self.grid_offset = None
|
||||
self.grid_spacing = None
|
||||
self.scroll_position = None
|
||||
self.label_pos = None
|
||||
self.text_size = None
|
||||
self.icon_size = None
|
||||
self.list_icon_size = None
|
||||
self.list_text_size = None
|
||||
self.list_scroll_position = None
|
||||
self.list_sort_by = None
|
||||
self.list_use_relative_dates = None
|
||||
self.list_calculate_all_sizes = None
|
||||
self.list_columns = None
|
||||
self.list_column_widths = None
|
||||
self.list_column_sort_directions = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
if not self.volume_label:
|
||||
msg = "volume-label must be set"
|
||||
raise OptionError(msg)
|
||||
if self.applications_shortcut:
|
||||
self._symlinks["Applications"] = "/Applications"
|
||||
if self.silent is None:
|
||||
self.silent = False
|
||||
|
||||
self.finalize_dmgbuild_options()
|
||||
|
||||
def finalize_dmgbuild_options(self) -> None:
|
||||
if self.background:
|
||||
self.background = self.background.strip()
|
||||
if self.background == "builtin-arrow" and (
|
||||
self.icon_locations or self.window_rect
|
||||
):
|
||||
msg = (
|
||||
"background='builtin-arrow' cannot be used with "
|
||||
"icon_locations or window_rect"
|
||||
)
|
||||
raise OptionError(msg)
|
||||
if not self.arrange_by:
|
||||
self.arrange_by = None
|
||||
if not self.grid_offset:
|
||||
self.grid_offset = (0, 0)
|
||||
if not self.grid_spacing:
|
||||
self.grid_spacing = 100
|
||||
if not self.scroll_position:
|
||||
self.scroll_position = (0, 0)
|
||||
if not self.label_pos:
|
||||
self.label_pos = "bottom"
|
||||
if not self.text_size:
|
||||
self.text_size = 16
|
||||
if not self.icon_size:
|
||||
self.icon_size = 128
|
||||
|
||||
def build_dmg(self) -> None:
|
||||
# Remove DMG if it already exists
|
||||
if os.path.exists(self.dmg_name):
|
||||
os.unlink(self.dmg_name)
|
||||
|
||||
# Make dist folder
|
||||
self.dist_dir = os.path.join(self.build_dir, "dist")
|
||||
if os.path.exists(self.dist_dir):
|
||||
shutil.rmtree(self.dist_dir)
|
||||
self.mkpath(self.dist_dir)
|
||||
|
||||
# Copy App Bundle
|
||||
dest_dir = os.path.join(
|
||||
self.dist_dir, os.path.basename(self.bundle_dir)
|
||||
)
|
||||
if self.silent:
|
||||
shutil.copytree(self.bundle_dir, dest_dir, symlinks=True)
|
||||
else:
|
||||
self.copy_tree(self.bundle_dir, dest_dir, preserve_symlinks=True)
|
||||
|
||||
# Add the App Bundle to the list of files
|
||||
self._files.append(self.bundle_dir)
|
||||
|
||||
# set the app_name for the application bundle
|
||||
app_name = os.path.basename(self.bundle_dir)
|
||||
# Set the defaults
|
||||
if (
|
||||
self.background == "builtin-arrow"
|
||||
and not self.icon_locations
|
||||
and not self.window_rect
|
||||
):
|
||||
self.icon_locations = {
|
||||
"Applications": (500, 120),
|
||||
app_name: (140, 120),
|
||||
}
|
||||
self.window_rect = ((100, 100), (640, 380))
|
||||
|
||||
executables = self.distribution.executables # type: list[Executable]
|
||||
executable: Executable = executables[0]
|
||||
if len(executables) > 1:
|
||||
self.warn(
|
||||
"using the first executable as entrypoint: "
|
||||
f"{executable.target_name}"
|
||||
)
|
||||
if executable.icon is None:
|
||||
icon_name = "setup.icns"
|
||||
icon_source_dir = os.path.dirname(cx_Freeze.icons.__file__)
|
||||
self.icon = os.path.join(icon_source_dir, icon_name)
|
||||
else:
|
||||
self.icon = os.path.abspath(executable.icon)
|
||||
|
||||
with open("settings.py", "w") as f:
|
||||
|
||||
def add_param(name, value) -> None:
|
||||
# if value is a string, add quotes
|
||||
if isinstance(value, (str)):
|
||||
f.write(f"{name} = '{value}'\n")
|
||||
else:
|
||||
f.write(f"{name} = {value}\n")
|
||||
|
||||
# Some fields expect and allow None, others don't
|
||||
# so we need to check for None and not add them for
|
||||
# the fields that don't allow it
|
||||
|
||||
# Disk Image Settings
|
||||
add_param("filename", self.dmg_name)
|
||||
add_param("volume_label", self.volume_label)
|
||||
add_param("format", self.format)
|
||||
add_param("filesystem", self.filesystem)
|
||||
add_param("size", self.size)
|
||||
|
||||
# Content Settings
|
||||
add_param("files", self._files)
|
||||
add_param("symlinks", self._symlinks)
|
||||
if self.hide:
|
||||
add_param("hide", self.hide)
|
||||
if self.hide_extensions:
|
||||
add_param("hide_extensions", self.hide_extensions)
|
||||
# Only one of these can be set
|
||||
if self.icon_locations:
|
||||
add_param("icon_locations", self.icon_locations)
|
||||
if self.icon:
|
||||
add_param("icon", self.icon)
|
||||
# We don't need to set this, as we only support icns
|
||||
# add param ( "badge_icon", self.badge_icon)
|
||||
|
||||
# Window Settings
|
||||
add_param("background", self.background)
|
||||
add_param("show_status_bar", self.show_status_bar)
|
||||
add_param("show_tab_view", self.show_tab_view)
|
||||
add_param("show_pathbar", self.show_path_bar)
|
||||
add_param("show_sidebar", self.show_sidebar)
|
||||
add_param("sidebar_width", self.sidebar_width)
|
||||
if self.window_rect:
|
||||
add_param("window_rect", self.window_rect)
|
||||
if self.default_view:
|
||||
add_param("default_view", self.default_view)
|
||||
|
||||
add_param("show_icon_preview", self.show_icon_preview)
|
||||
add_param(
|
||||
"include_icon_view_settings", self.include_icon_view_settings
|
||||
)
|
||||
add_param(
|
||||
"include_list_view_settings", self.include_list_view_settings
|
||||
)
|
||||
|
||||
# Icon View Settings\
|
||||
add_param("arrange_by", self.arrange_by)
|
||||
add_param("grid_offset", self.grid_offset)
|
||||
add_param("grid_spacing", self.grid_spacing)
|
||||
add_param("scroll_position", self.scroll_position)
|
||||
add_param("label_pos", self.label_pos)
|
||||
if self.text_size:
|
||||
add_param("text_size", self.text_size)
|
||||
if self.icon_size:
|
||||
add_param("icon_size", self.icon_size)
|
||||
if self.icon_locations:
|
||||
add_param("icon_locations", self.icon_locations)
|
||||
|
||||
# List View Settings
|
||||
if self.list_icon_size:
|
||||
add_param("list_icon_size", self.list_icon_size)
|
||||
if self.list_text_size:
|
||||
add_param("list_text_size", self.list_text_size)
|
||||
if self.list_scroll_position:
|
||||
add_param("list_scroll_position", self.list_scroll_position)
|
||||
add_param("list_sort_by", self.list_sort_by)
|
||||
add_param("list_use_relative_dates", self.list_use_relative_dates)
|
||||
add_param(
|
||||
"list_calculate_all_sizes", self.list_calculate_all_sizes
|
||||
)
|
||||
if self.list_columns:
|
||||
add_param("list_columns", self.list_columns)
|
||||
if self.list_column_widths:
|
||||
add_param("list_column_widths", self.list_column_widths)
|
||||
if self.list_column_sort_directions:
|
||||
add_param(
|
||||
"list_column_sort_directions",
|
||||
self.list_column_sort_directions,
|
||||
)
|
||||
|
||||
# License Settings
|
||||
add_param("license", self.license)
|
||||
|
||||
def log_handler(msg: dict[str, str]) -> None:
|
||||
if not self.silent:
|
||||
loggable = ",".join(
|
||||
f"{key}: {value}" for key, value in msg.items()
|
||||
)
|
||||
self.announce(loggable)
|
||||
|
||||
build_dmg(
|
||||
self.dmg_name,
|
||||
self.volume_label,
|
||||
"settings.py",
|
||||
callback=log_handler,
|
||||
)
|
||||
|
||||
def run(self) -> None:
|
||||
# Create the application bundle
|
||||
self.run_command("bdist_mac")
|
||||
|
||||
# Find the location of the application bundle and the build dir
|
||||
self.bundle_dir = self.get_finalized_command("bdist_mac").bundle_dir
|
||||
self.build_dir = self.get_finalized_command("build_exe").build_base
|
||||
|
||||
# Set the file name of the DMG to be built
|
||||
self.dmg_name = os.path.join(
|
||||
self.build_dir, self.volume_label + ".dmg"
|
||||
)
|
||||
|
||||
self.execute(self.build_dmg, ())
|
||||
529
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_mac.py
Normal file
529
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_mac.py
Normal file
@@ -0,0 +1,529 @@
|
||||
"""Implements the 'bdist_mac' commands (create macOS
|
||||
app blundle).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import plistlib
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import ClassVar
|
||||
|
||||
from setuptools import Command
|
||||
|
||||
from cx_Freeze.common import normalize_to_list
|
||||
from cx_Freeze.darwintools import (
|
||||
apply_adhoc_signature,
|
||||
change_load_reference,
|
||||
isMachOFile,
|
||||
)
|
||||
from cx_Freeze.exception import OptionError
|
||||
|
||||
__all__ = ["bdist_mac"]
|
||||
|
||||
|
||||
class bdist_mac(Command):
|
||||
"""Create a Mac application bundle."""
|
||||
|
||||
description = "create a Mac application bundle"
|
||||
|
||||
plist_items: list[tuple[str, str]]
|
||||
include_frameworks: list[str]
|
||||
include_resources: list[str]
|
||||
|
||||
user_options: ClassVar[list[tuple[str, str | None, str]]] = [
|
||||
("iconfile=", None, "Path to an icns icon file for the application."),
|
||||
(
|
||||
"qt-menu-nib=",
|
||||
None,
|
||||
"Location of qt_menu.nib folder for Qt "
|
||||
"applications. Will be auto-detected by default.",
|
||||
),
|
||||
(
|
||||
"bundle-name=",
|
||||
None,
|
||||
"File name for the bundle application "
|
||||
"without the .app extension.",
|
||||
),
|
||||
(
|
||||
"plist-items=",
|
||||
None,
|
||||
"A list of key-value pairs (type: list[tuple[str, str]]) to "
|
||||
"be added to the app bundle Info.plist file.",
|
||||
),
|
||||
(
|
||||
"custom-info-plist=",
|
||||
None,
|
||||
"File to be used as the Info.plist in "
|
||||
"the app bundle. A basic one will be generated by default.",
|
||||
),
|
||||
(
|
||||
"include-frameworks=",
|
||||
None,
|
||||
"A comma separated list of Framework "
|
||||
"directories to include in the app bundle.",
|
||||
),
|
||||
(
|
||||
"include-resources=",
|
||||
None,
|
||||
"A list of tuples of additional "
|
||||
"files to include in the app bundle's resources directory, with "
|
||||
"the first element being the source, and second the destination "
|
||||
"file or directory name.",
|
||||
),
|
||||
(
|
||||
"codesign-identity=",
|
||||
None,
|
||||
"The identity of the key to be used to sign the app bundle.",
|
||||
),
|
||||
(
|
||||
"codesign-entitlements=",
|
||||
None,
|
||||
"The path to an entitlements file "
|
||||
"to use for your application's code signature.",
|
||||
),
|
||||
(
|
||||
"codesign-deep=",
|
||||
None,
|
||||
"Boolean for whether to codesign using the --deep option.",
|
||||
),
|
||||
(
|
||||
"codesign-timestamp",
|
||||
None,
|
||||
"Boolean for whether to codesign using the --timestamp option.",
|
||||
),
|
||||
(
|
||||
"codesign-resource-rules",
|
||||
None,
|
||||
"Plist file to be passed to "
|
||||
"codesign's --resource-rules option.",
|
||||
),
|
||||
(
|
||||
"absolute-reference-path=",
|
||||
None,
|
||||
"Path to use for all referenced "
|
||||
"libraries instead of @executable_path.",
|
||||
),
|
||||
(
|
||||
"codesign-verify",
|
||||
None,
|
||||
"Boolean to verify codesign of the .app bundle using the codesign "
|
||||
"command",
|
||||
),
|
||||
(
|
||||
"spctl-assess",
|
||||
None,
|
||||
"Boolean to verify codesign of the .app bundle using the spctl "
|
||||
"command",
|
||||
),
|
||||
(
|
||||
"codesign-strict=",
|
||||
None,
|
||||
"Boolean for whether to codesign using the --strict option.",
|
||||
),
|
||||
(
|
||||
"codesign-options=",
|
||||
None,
|
||||
"Option flags to be embedded in the code signature",
|
||||
),
|
||||
]
|
||||
|
||||
def initialize_options(self) -> None:
|
||||
self.list_options = [
|
||||
"plist_items",
|
||||
"include_frameworks",
|
||||
"include_resources",
|
||||
]
|
||||
for option in self.list_options:
|
||||
setattr(self, option, [])
|
||||
|
||||
self.absolute_reference_path = None
|
||||
self.bundle_name = self.distribution.get_fullname()
|
||||
self.codesign_deep = None
|
||||
self.codesign_entitlements = None
|
||||
self.codesign_identity = None
|
||||
self.codesign_timestamp = None
|
||||
self.codesign_strict = None
|
||||
self.codesign_options = None
|
||||
self.codesign_resource_rules = None
|
||||
self.codesign_verify = None
|
||||
self.spctl_assess = None
|
||||
self.custom_info_plist = None
|
||||
self.iconfile = None
|
||||
self.qt_menu_nib = False
|
||||
|
||||
self.build_base = None
|
||||
self.build_dir = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
# Make sure all options of multiple values are lists
|
||||
for option in self.list_options:
|
||||
setattr(self, option, normalize_to_list(getattr(self, option)))
|
||||
for item in self.plist_items:
|
||||
if not isinstance(item, tuple) or len(item) != 2:
|
||||
msg = (
|
||||
"Error, plist_items must be a list of key, value pairs "
|
||||
"(list[tuple[str, str]]) (bad list item)."
|
||||
)
|
||||
raise OptionError(msg)
|
||||
|
||||
# Define the paths within the application bundle
|
||||
self.set_undefined_options(
|
||||
"build_exe",
|
||||
("build_base", "build_base"),
|
||||
("build_exe", "build_dir"),
|
||||
)
|
||||
self.bundle_dir = os.path.join(
|
||||
self.build_base, f"{self.bundle_name}.app"
|
||||
)
|
||||
self.contents_dir = os.path.join(self.bundle_dir, "Contents")
|
||||
self.bin_dir = os.path.join(self.contents_dir, "MacOS")
|
||||
self.frameworks_dir = os.path.join(self.contents_dir, "Frameworks")
|
||||
self.resources_dir = os.path.join(self.contents_dir, "Resources")
|
||||
self.helpers_dir = os.path.join(self.contents_dir, "Helpers")
|
||||
|
||||
def create_plist(self) -> None:
|
||||
"""Create the Contents/Info.plist file."""
|
||||
# Use custom plist if supplied, otherwise create a simple default.
|
||||
if self.custom_info_plist:
|
||||
with open(self.custom_info_plist, "rb") as file:
|
||||
contents = plistlib.load(file)
|
||||
else:
|
||||
contents = {
|
||||
"CFBundleIconFile": "icon.icns",
|
||||
"CFBundleDevelopmentRegion": "English",
|
||||
"CFBundleIdentifier": self.bundle_name,
|
||||
# Specify that bundle is an application bundle
|
||||
"CFBundlePackageType": "APPL",
|
||||
# Cause application to run in high-resolution mode by default
|
||||
# (Without this, applications run from application bundle may
|
||||
# be pixelated)
|
||||
"NSHighResolutionCapable": "True",
|
||||
}
|
||||
|
||||
# Ensure CFBundleExecutable is set correctly
|
||||
contents["CFBundleExecutable"] = self.bundle_executable
|
||||
|
||||
# add custom items to the plist file
|
||||
for key, value in self.plist_items:
|
||||
contents[key] = value
|
||||
|
||||
with open(os.path.join(self.contents_dir, "Info.plist"), "wb") as file:
|
||||
plistlib.dump(contents, file)
|
||||
|
||||
def set_absolute_reference_paths(self, path=None) -> None:
|
||||
"""For all files in Contents/MacOS, set their linked library paths to
|
||||
be absolute paths using the given path instead of @executable_path.
|
||||
"""
|
||||
if not path:
|
||||
path = self.absolute_reference_path
|
||||
|
||||
files = os.listdir(self.bin_dir)
|
||||
|
||||
for filename in files:
|
||||
filepath = os.path.join(self.bin_dir, filename)
|
||||
|
||||
# Skip some file types
|
||||
if filepath[-1:] in ("txt", "zip") or os.path.isdir(filepath):
|
||||
continue
|
||||
|
||||
out = subprocess.check_output(
|
||||
("otool", "-L", filepath), encoding="utf_8"
|
||||
)
|
||||
for line in out.splitlines()[1:]:
|
||||
lib = line.lstrip("\t").split(" (compat")[0]
|
||||
|
||||
if lib.startswith("@executable_path"):
|
||||
replacement = lib.replace("@executable_path", path)
|
||||
|
||||
path, name = os.path.split(replacement)
|
||||
|
||||
# see if we provide the referenced file;
|
||||
# if so, change the reference
|
||||
if name in files:
|
||||
change_load_reference(filepath, lib, replacement)
|
||||
apply_adhoc_signature(filepath)
|
||||
|
||||
def find_qt_menu_nib(self) -> str | None:
|
||||
"""Returns a location of a qt_menu.nib folder, or None if this is not
|
||||
a Qt application.
|
||||
"""
|
||||
if self.qt_menu_nib:
|
||||
return self.qt_menu_nib
|
||||
if any(n.startswith("PyQt4.QtCore") for n in os.listdir(self.bin_dir)):
|
||||
name = "PyQt4"
|
||||
elif any(
|
||||
n.startswith("PySide.QtCore") for n in os.listdir(self.bin_dir)
|
||||
):
|
||||
name = "PySide"
|
||||
else:
|
||||
return None
|
||||
|
||||
qtcore = __import__(name, fromlist=["QtCore"]).QtCore
|
||||
libpath = str(
|
||||
qtcore.QLibraryInfo.location(qtcore.QLibraryInfo.LibrariesPath)
|
||||
)
|
||||
for subpath in [
|
||||
"QtGui.framework/Resources/qt_menu.nib",
|
||||
"Resources/qt_menu.nib",
|
||||
]:
|
||||
path = os.path.join(libpath, subpath)
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
# Last resort: fixed paths (macports)
|
||||
for path in [
|
||||
"/opt/local/Library/Frameworks/QtGui.framework/Versions/"
|
||||
"4/Resources/qt_menu.nib"
|
||||
]:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
print("Could not find qt_menu.nib")
|
||||
msg = "Could not find qt_menu.nib"
|
||||
raise OSError(msg)
|
||||
|
||||
def prepare_qt_app(self) -> None:
|
||||
"""Add resource files for a Qt application. Should do nothing if the
|
||||
application does not use QtCore.
|
||||
"""
|
||||
qt_conf = os.path.join(self.resources_dir, "qt.conf")
|
||||
qt_conf_2 = os.path.join(self.resources_dir, "qt_bdist_mac.conf")
|
||||
if os.path.exists(qt_conf_2):
|
||||
self.execute(
|
||||
shutil.move,
|
||||
(qt_conf_2, qt_conf),
|
||||
msg=f"moving {qt_conf_2} -> {qt_conf}",
|
||||
)
|
||||
|
||||
nib_locn = self.find_qt_menu_nib()
|
||||
if nib_locn is None:
|
||||
return
|
||||
|
||||
# Copy qt_menu.nib
|
||||
self.copy_tree(
|
||||
nib_locn, os.path.join(self.resources_dir, "qt_menu.nib")
|
||||
)
|
||||
|
||||
# qt.conf needs to exist, but needn't have any content
|
||||
if not os.path.exists(qt_conf):
|
||||
with open(qt_conf, "wb"):
|
||||
pass
|
||||
|
||||
def run(self) -> None:
|
||||
self.run_command("build_exe")
|
||||
|
||||
# Remove App if it already exists
|
||||
# ( avoids confusing issues where prior builds persist! )
|
||||
if os.path.exists(self.bundle_dir):
|
||||
self.execute(
|
||||
shutil.rmtree,
|
||||
(self.bundle_dir,),
|
||||
msg=f"staging - removed existing '{self.bundle_dir}'",
|
||||
)
|
||||
|
||||
# Find the executable name
|
||||
executable = self.distribution.executables[0].target_name
|
||||
_, self.bundle_executable = os.path.split(executable)
|
||||
print(f"Executable name: {self.build_dir}/{executable}")
|
||||
|
||||
# Build the app directory structure
|
||||
self.mkpath(self.bin_dir) # /MacOS
|
||||
self.mkpath(self.frameworks_dir) # /Frameworks
|
||||
self.mkpath(self.resources_dir) # /Resources
|
||||
|
||||
# Copy the full build_exe to Contents/Resources
|
||||
self.copy_tree(self.build_dir, self.resources_dir)
|
||||
|
||||
# Move only executables in Contents/Resources to Contents/MacOS
|
||||
for executable in self.distribution.executables:
|
||||
source = os.path.join(self.resources_dir, executable.target_name)
|
||||
target = os.path.join(self.bin_dir, executable.target_name)
|
||||
self.move_file(source, target)
|
||||
|
||||
# Make symlink between folders under Resources such as lib and others
|
||||
# specified by the user in include_files and Contents/MacOS so we can
|
||||
# use non-relative reference paths to pass codesign...
|
||||
for filename in os.listdir(self.resources_dir):
|
||||
target = os.path.join(self.resources_dir, filename)
|
||||
if os.path.isdir(target):
|
||||
origin = os.path.join(self.bin_dir, filename)
|
||||
relative_reference = os.path.relpath(target, self.bin_dir)
|
||||
self.execute(
|
||||
os.symlink,
|
||||
(relative_reference, origin, True),
|
||||
msg=f"linking {origin} -> {relative_reference}",
|
||||
)
|
||||
|
||||
# Copy the icon
|
||||
if self.iconfile:
|
||||
self.copy_file(
|
||||
self.iconfile, os.path.join(self.resources_dir, "icon.icns")
|
||||
)
|
||||
|
||||
# Copy in Frameworks
|
||||
for framework in self.include_frameworks:
|
||||
self.copy_tree(
|
||||
framework,
|
||||
os.path.join(self.frameworks_dir, os.path.basename(framework)),
|
||||
)
|
||||
|
||||
# Copy in Resources
|
||||
for resource, destination in self.include_resources:
|
||||
if os.path.isdir(resource):
|
||||
self.copy_tree(
|
||||
resource, os.path.join(self.resources_dir, destination)
|
||||
)
|
||||
else:
|
||||
parent_dirs = os.path.dirname(
|
||||
os.path.join(self.resources_dir, destination)
|
||||
)
|
||||
os.makedirs(parent_dirs, exist_ok=True)
|
||||
self.copy_file(
|
||||
resource, os.path.join(self.resources_dir, destination)
|
||||
)
|
||||
|
||||
# Create the Info.plist file
|
||||
self.execute(self.create_plist, (), msg="creating Contents/Info.plist")
|
||||
|
||||
# Make library references absolute if enabled
|
||||
if self.absolute_reference_path:
|
||||
self.execute(
|
||||
self.set_absolute_reference_paths,
|
||||
(),
|
||||
msg="set absolute reference path "
|
||||
f"'{self.absolute_reference_path}",
|
||||
)
|
||||
|
||||
# For a Qt application, run some tweaks
|
||||
self.execute(self.prepare_qt_app, ())
|
||||
|
||||
# Move Contents/Resources/share/*.app to Contents/Helpers
|
||||
share_dir = os.path.join(self.resources_dir, "share")
|
||||
if os.path.isdir(share_dir):
|
||||
for filename in os.listdir(share_dir):
|
||||
if not filename.endswith(".app"):
|
||||
continue
|
||||
# create /Helpers only if required
|
||||
self.mkpath(self.helpers_dir)
|
||||
source = os.path.join(share_dir, filename)
|
||||
target = os.path.join(self.helpers_dir, filename)
|
||||
self.execute(
|
||||
shutil.move,
|
||||
(source, target),
|
||||
msg=f"moving {source} -> {target}",
|
||||
)
|
||||
if os.path.isdir(target):
|
||||
origin = os.path.join(target, "Contents", "MacOS", "lib")
|
||||
relative_reference = os.path.relpath(
|
||||
os.path.join(self.resources_dir, "lib"),
|
||||
os.path.join(target, "Contents", "MacOS"),
|
||||
)
|
||||
self.execute(
|
||||
os.symlink,
|
||||
(relative_reference, origin, True),
|
||||
msg=f"linking {origin} -> {relative_reference}",
|
||||
)
|
||||
|
||||
# Sign the app bundle if a key is specified
|
||||
self.execute(
|
||||
self._codesign,
|
||||
(self.bundle_dir,),
|
||||
msg=f"sign: '{self.bundle_dir}'",
|
||||
)
|
||||
|
||||
def _codesign(self, root_path) -> None:
|
||||
"""Run codesign on all .so, .dylib and binary files in reverse order.
|
||||
Signing from inside-out.
|
||||
"""
|
||||
if not self.codesign_identity:
|
||||
return
|
||||
|
||||
binaries_to_sign = []
|
||||
|
||||
# Identify all binary files
|
||||
for dirpath, _, filenames in os.walk(root_path):
|
||||
for filename in filenames:
|
||||
full_path = Path(os.path.join(dirpath, filename))
|
||||
|
||||
if isMachOFile(full_path):
|
||||
binaries_to_sign.append(full_path)
|
||||
|
||||
# Sort files by depth, so we sign the deepest files first
|
||||
binaries_to_sign.sort(key=lambda x: str(x).count(os.sep), reverse=True)
|
||||
|
||||
for binary_path in binaries_to_sign:
|
||||
self._codesign_file(binary_path, self._get_sign_args())
|
||||
|
||||
self._verify_signature()
|
||||
print("Finished .app signing")
|
||||
|
||||
def _get_sign_args(self) -> list[str]:
|
||||
signargs = ["codesign", "--sign", self.codesign_identity, "--force"]
|
||||
|
||||
if self.codesign_timestamp:
|
||||
signargs.append("--timestamp")
|
||||
|
||||
if self.codesign_strict:
|
||||
signargs.append(f"--strict={self.codesign_strict}")
|
||||
|
||||
if self.codesign_deep:
|
||||
signargs.append("--deep")
|
||||
|
||||
if self.codesign_options:
|
||||
signargs.append("--options")
|
||||
signargs.append(self.codesign_options)
|
||||
|
||||
if self.codesign_entitlements:
|
||||
signargs.append("--entitlements")
|
||||
signargs.append(self.codesign_entitlements)
|
||||
return signargs
|
||||
|
||||
def _codesign_file(self, file_path, sign_args) -> None:
|
||||
print(f"Signing file: {file_path}")
|
||||
sign_args.append(file_path)
|
||||
subprocess.run(sign_args, check=False)
|
||||
|
||||
def _verify_signature(self) -> None:
|
||||
if self.codesign_verify:
|
||||
verify_args = [
|
||||
"codesign",
|
||||
"-vvv",
|
||||
"--deep",
|
||||
"--strict",
|
||||
self.bundle_dir,
|
||||
]
|
||||
print("Running codesign verification")
|
||||
result = subprocess.run(
|
||||
verify_args, capture_output=True, text=True, check=False
|
||||
)
|
||||
print("ExitCode:", result.returncode)
|
||||
print(" stdout:", result.stdout)
|
||||
print(" stderr:", result.stderr)
|
||||
|
||||
if self.spctl_assess:
|
||||
spctl_args = [
|
||||
"spctl",
|
||||
"--assess",
|
||||
"--raw",
|
||||
"--verbose=10",
|
||||
"--type",
|
||||
"exec",
|
||||
self.bundle_dir,
|
||||
]
|
||||
try:
|
||||
completed_process = subprocess.run(
|
||||
spctl_args,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
print(
|
||||
"spctl command's output: "
|
||||
f"{completed_process.stdout.decode()}"
|
||||
)
|
||||
except subprocess.CalledProcessError as error:
|
||||
print(f"spctl check got an error: {error.stdout.decode()}")
|
||||
raise
|
||||
1198
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_msi.py
Normal file
1198
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_msi.py
Normal file
File diff suppressed because it is too large
Load Diff
562
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_rpm.py
Normal file
562
venv3_12/Lib/site-packages/cx_Freeze/command/bdist_rpm.py
Normal file
@@ -0,0 +1,562 @@
|
||||
"""Implements the 'bdist_rpm' command (create RPM binary distributions).
|
||||
|
||||
Borrowed from distutils.command.bdist_rpm of Python 3.10 and merged with
|
||||
bdist_rpm subclass of cx_Freeze 6.10.
|
||||
|
||||
https://rpm.org/documentation.html
|
||||
https://rpm-packaging-guide.github.io/
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
import tarfile
|
||||
from subprocess import CalledProcessError, check_output
|
||||
from sysconfig import get_python_version
|
||||
from typing import ClassVar
|
||||
|
||||
from setuptools import Command
|
||||
|
||||
from cx_Freeze._compat import IS_CONDA
|
||||
from cx_Freeze.exception import ExecError, FileError, PlatformError
|
||||
|
||||
__all__ = ["bdist_rpm"]
|
||||
|
||||
|
||||
class bdist_rpm(Command):
|
||||
"""Create an RPM distribution."""
|
||||
|
||||
description = "create an RPM distribution"
|
||||
|
||||
user_options: ClassVar[list[tuple[str, str | None, str]]] = [
|
||||
(
|
||||
"bdist-base=",
|
||||
None,
|
||||
"base directory for creating built distributions",
|
||||
),
|
||||
(
|
||||
"rpm-base=",
|
||||
None,
|
||||
"base directory for creating RPMs "
|
||||
'[defaults to "rpm" under "--bdist-base"]',
|
||||
),
|
||||
(
|
||||
"dist-dir=",
|
||||
"d",
|
||||
"directory to put final RPM files in "
|
||||
"(and .spec files if --spec-only)",
|
||||
),
|
||||
("spec-only", None, "only regenerate spec file"),
|
||||
# More meta-data: too RPM-specific to put in the setup script,
|
||||
# but needs to go in the .spec file -- so we make these options
|
||||
# to "bdist_rpm". The idea is that packagers would put this
|
||||
# info in pyproject.toml or setup.cfg, although they are of course free
|
||||
# to supply it on the command line.
|
||||
(
|
||||
"distribution-name=",
|
||||
None,
|
||||
"name of the (Linux) distribution to which this "
|
||||
"RPM applies (*not* the name of the module distribution!)",
|
||||
),
|
||||
(
|
||||
"group=",
|
||||
None,
|
||||
'package classification [default: "Development/Libraries"]',
|
||||
),
|
||||
("release=", None, "RPM release number"),
|
||||
("serial=", None, "RPM serial number"),
|
||||
(
|
||||
"vendor=",
|
||||
None,
|
||||
'RPM "vendor" (eg. "Joe Blow <joe@example.com>") '
|
||||
"[default: maintainer or author from setup script]",
|
||||
),
|
||||
(
|
||||
"packager=",
|
||||
None,
|
||||
'RPM packager (eg. "Jane Doe <jane@example.net>") '
|
||||
"[default: same as vendor]",
|
||||
),
|
||||
(
|
||||
"doc-files=",
|
||||
None,
|
||||
"list of documentation files (space or comma-separated)",
|
||||
),
|
||||
("changelog=", None, "RPM changelog"),
|
||||
("icon=", None, "name of icon file"),
|
||||
("provides=", None, "capabilities provided by this package"),
|
||||
("requires=", None, "capabilities required by this package"),
|
||||
("conflicts=", None, "capabilities which conflict with this package"),
|
||||
(
|
||||
"build-requires=",
|
||||
None,
|
||||
"capabilities required to build this package",
|
||||
),
|
||||
("obsoletes=", None, "capabilities made obsolete by this package"),
|
||||
("no-autoreq", None, "do not automatically calculate dependencies"),
|
||||
# Actions to take when building RPM
|
||||
("keep-temp", "k", "don't clean up RPM build directory"),
|
||||
("no-keep-temp", None, "clean up RPM build directory [default]"),
|
||||
# Add the hooks necessary for specifying custom scripts
|
||||
(
|
||||
"prep-script=",
|
||||
None,
|
||||
"Specify a script for the PREP phase of RPM building",
|
||||
),
|
||||
(
|
||||
"build-script=",
|
||||
None,
|
||||
"Specify a script for the BUILD phase of RPM building",
|
||||
),
|
||||
(
|
||||
"pre-install=",
|
||||
None,
|
||||
"Specify a script for the pre-INSTALL phase of RPM building",
|
||||
),
|
||||
(
|
||||
"install-script=",
|
||||
None,
|
||||
"Specify a script for the INSTALL phase of RPM building",
|
||||
),
|
||||
(
|
||||
"post-install=",
|
||||
None,
|
||||
"Specify a script for the post-INSTALL phase of RPM building",
|
||||
),
|
||||
(
|
||||
"pre-uninstall=",
|
||||
None,
|
||||
"Specify a script for the pre-UNINSTALL phase of RPM building",
|
||||
),
|
||||
(
|
||||
"post-uninstall=",
|
||||
None,
|
||||
"Specify a script for the post-UNINSTALL phase of RPM building",
|
||||
),
|
||||
(
|
||||
"clean-script=",
|
||||
None,
|
||||
"Specify a script for the CLEAN phase of RPM building",
|
||||
),
|
||||
(
|
||||
"verify-script=",
|
||||
None,
|
||||
"Specify a script for the VERIFY phase of the RPM build",
|
||||
),
|
||||
("quiet", "q", "Run the INSTALL phase of RPM building in quiet mode"),
|
||||
("debug", "g", "Run in debug mode"),
|
||||
]
|
||||
|
||||
boolean_options: ClassVar[list[str]] = [
|
||||
"keep-temp",
|
||||
"no-autoreq",
|
||||
"quiet",
|
||||
"debug",
|
||||
]
|
||||
|
||||
negative_opt: ClassVar[dict[str, str]] = {
|
||||
"no-keep-temp": "keep-temp",
|
||||
}
|
||||
|
||||
def initialize_options(self) -> None:
|
||||
self.bdist_base = None
|
||||
self.dist_dir = None
|
||||
|
||||
self.rpm_base = None
|
||||
self.spec_only = None
|
||||
|
||||
self.distribution_name = None
|
||||
self.group = None
|
||||
self.release = None
|
||||
self.serial = None
|
||||
self.vendor = None
|
||||
self.packager = None
|
||||
self.doc_files = None
|
||||
self.changelog = None
|
||||
self.icon = None
|
||||
|
||||
self.prep_script = None
|
||||
self.build_script = None
|
||||
self.install_script = None
|
||||
self.clean_script = None
|
||||
self.verify_script = None
|
||||
self.pre_install = None
|
||||
self.post_install = None
|
||||
self.pre_uninstall = None
|
||||
self.post_uninstall = None
|
||||
self.prep = None
|
||||
self.provides = None
|
||||
self.requires = None
|
||||
self.conflicts = None
|
||||
self.build_requires = None
|
||||
self.obsoletes = None
|
||||
|
||||
self.keep_temp = 0
|
||||
self.no_autoreq = 0
|
||||
|
||||
self.quiet = 0
|
||||
self.debug = 0
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
if os.name != "posix":
|
||||
msg = (
|
||||
"don't know how to create RPM "
|
||||
f"distributions on platform {os.name}"
|
||||
)
|
||||
raise PlatformError(msg)
|
||||
|
||||
self._rpm = shutil.which("rpm")
|
||||
self._rpmbuild = shutil.which("rpmbuild")
|
||||
if not self._rpmbuild:
|
||||
msg = "failed to find rpmbuild for this platform."
|
||||
raise PlatformError(msg)
|
||||
|
||||
self.set_undefined_options(
|
||||
"bdist",
|
||||
("bdist_base", "bdist_base"),
|
||||
("dist_dir", "dist_dir"),
|
||||
)
|
||||
if self.rpm_base is None:
|
||||
self.rpm_base = os.path.join(self.bdist_base, "rpm")
|
||||
|
||||
self.finalize_package_data()
|
||||
|
||||
def finalize_package_data(self) -> None:
|
||||
self.ensure_string("group", "Development/Libraries")
|
||||
contact = self.distribution.get_contact() or "UNKNOWN"
|
||||
contact_email = self.distribution.get_contact_email() or "UNKNOWN"
|
||||
self.ensure_string("vendor", f"{contact} <{contact_email}>")
|
||||
self.ensure_string("packager")
|
||||
self.ensure_string_list("doc_files")
|
||||
if isinstance(self.doc_files, list):
|
||||
doc_files = set(self.doc_files)
|
||||
for readme in ("README", "README.txt"):
|
||||
if os.path.exists(readme) and readme not in doc_files:
|
||||
self.doc_files.append(readme)
|
||||
|
||||
self.ensure_string("release", "1")
|
||||
self.ensure_string("serial") # should it be an int?
|
||||
|
||||
self.ensure_string("distribution_name")
|
||||
|
||||
self.ensure_string("changelog")
|
||||
# Format changelog correctly
|
||||
self.changelog = self._format_changelog(self.changelog)
|
||||
|
||||
self.ensure_filename("icon")
|
||||
|
||||
self.ensure_filename("prep_script")
|
||||
self.ensure_filename("build_script")
|
||||
self.ensure_filename("install_script")
|
||||
self.ensure_filename("clean_script")
|
||||
self.ensure_filename("verify_script")
|
||||
self.ensure_filename("pre_install")
|
||||
self.ensure_filename("post_install")
|
||||
self.ensure_filename("pre_uninstall")
|
||||
self.ensure_filename("post_uninstall")
|
||||
|
||||
# Now *this* is some meta-data that belongs in the setup script...
|
||||
self.ensure_string_list("provides")
|
||||
self.ensure_string_list("requires")
|
||||
self.ensure_string_list("conflicts")
|
||||
self.ensure_string_list("build_requires")
|
||||
self.ensure_string_list("obsoletes")
|
||||
|
||||
def run(self) -> None:
|
||||
if self.debug:
|
||||
print("before _get_package_data():")
|
||||
print("vendor =", self.vendor)
|
||||
print("packager =", self.packager)
|
||||
print("doc_files =", self.doc_files)
|
||||
print("changelog =", self.changelog)
|
||||
|
||||
# make directories
|
||||
if self.spec_only:
|
||||
spec_dir = self.dist_dir
|
||||
else:
|
||||
rpm_dir = {}
|
||||
for data in ("SOURCES", "SPECS", "BUILD", "RPMS", "SRPMS"):
|
||||
rpm_dir[data] = os.path.join(self.rpm_base, data)
|
||||
self.mkpath(rpm_dir[data])
|
||||
spec_dir = rpm_dir["SPECS"]
|
||||
self.mkpath(self.dist_dir)
|
||||
|
||||
# Spec file goes into 'dist_dir' if '--spec-only specified',
|
||||
# build/rpm.<plat> otherwise.
|
||||
distribution_name = self.distribution.get_name()
|
||||
spec_path = os.path.join(spec_dir, f"{distribution_name}.spec")
|
||||
self.execute(
|
||||
write_file,
|
||||
(spec_path, self._make_spec_file()),
|
||||
f"writing '{spec_path}'",
|
||||
)
|
||||
|
||||
if self.spec_only: # stop if requested
|
||||
return
|
||||
|
||||
# Make a source distribution and copy to SOURCES directory with
|
||||
# optional icon.
|
||||
def exclude_filter(info: tarfile.TarInfo) -> tarfile.TarInfo | None:
|
||||
if (
|
||||
os.path.basename(info.name) in ("build", "dist")
|
||||
and info.isdir()
|
||||
):
|
||||
return None
|
||||
return info
|
||||
|
||||
name = self.distribution.get_name()
|
||||
version = self.distribution.get_version()
|
||||
source = f"{name}-{version}"
|
||||
source_dir = rpm_dir["SOURCES"]
|
||||
source_fullname = os.path.join(source_dir, source + ".tar.gz")
|
||||
with tarfile.open(source_fullname, "w:gz") as tar:
|
||||
tar.add(".", source, filter=exclude_filter)
|
||||
if self.icon:
|
||||
if os.path.exists(self.icon):
|
||||
self.copy_file(self.icon, source_dir)
|
||||
else:
|
||||
msg = f"icon file {self.icon!r} does not exist"
|
||||
raise FileError(msg)
|
||||
|
||||
# build package, binary only (-bb)
|
||||
logging.info("building RPMs")
|
||||
rpm_cmd = [self._rpmbuild, "-bb"]
|
||||
if not self.keep_temp:
|
||||
rpm_cmd.append("--clean")
|
||||
|
||||
if self.quiet:
|
||||
rpm_cmd.append("--quiet")
|
||||
|
||||
rpm_cmd.append(spec_path)
|
||||
# Determine the binary rpm names that should be built out of this spec
|
||||
# file
|
||||
# Note that some of these may not be really built (if the file
|
||||
# list is empty)
|
||||
nvr_string = "%{name}-%{version}-%{release}"
|
||||
src_rpm = nvr_string + ".src.rpm"
|
||||
non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm"
|
||||
q_cmd = [
|
||||
self._rpm,
|
||||
"-q",
|
||||
"--qf",
|
||||
rf"{src_rpm} {non_src_rpm}\n",
|
||||
"--specfile",
|
||||
spec_path,
|
||||
]
|
||||
try:
|
||||
out = check_output(q_cmd, text=True)
|
||||
except CalledProcessError as exc:
|
||||
msg = f"Failed to execute: {' '.join(q_cmd)!r}"
|
||||
raise ExecError(msg) from exc
|
||||
|
||||
binary_rpms = []
|
||||
for line in out.splitlines():
|
||||
rows = line.split()
|
||||
assert len(rows) == 2 # noqa: S101
|
||||
binary_rpms.append(rows[1])
|
||||
|
||||
self.spawn(rpm_cmd)
|
||||
|
||||
if not self.dry_run:
|
||||
pyversion = get_python_version()
|
||||
|
||||
for binary_rpm in binary_rpms:
|
||||
rpm = os.path.join(rpm_dir["RPMS"], binary_rpm)
|
||||
if os.path.exists(rpm):
|
||||
self.move_file(rpm, self.dist_dir)
|
||||
filename = os.path.join(
|
||||
self.dist_dir, os.path.basename(rpm)
|
||||
)
|
||||
self.distribution.dist_files.append(
|
||||
("bdist_rpm", pyversion, filename)
|
||||
)
|
||||
|
||||
def _make_spec_file(self) -> list[str]:
|
||||
"""Generate the text of an RPM spec file and return it as a
|
||||
list of strings (one per line).
|
||||
"""
|
||||
# definitions and headers
|
||||
dist = self.distribution
|
||||
spec_file = [
|
||||
f"%define _topdir {os.path.abspath(self.rpm_base)}",
|
||||
# cx_Freeze specific
|
||||
"%define __prelink_undo_cmd %{nil}",
|
||||
"%define __strip /bin/true",
|
||||
"",
|
||||
f"%define name {dist.get_name()}",
|
||||
f"%define version {dist.get_version().replace('-', '_')}",
|
||||
f"%define unmangled_version {dist.get_version()}",
|
||||
f"%define release {self.release.replace('-', '_')}",
|
||||
"",
|
||||
f"Summary: {dist.get_description() or 'UNKNOWN'}",
|
||||
"Name: %{name}",
|
||||
"Version: %{version}",
|
||||
"Release: %{release}",
|
||||
f"License: {dist.get_license() or 'UNKNOWN'}",
|
||||
f"Group: {self.group}",
|
||||
"BuildRoot: %{buildroot}",
|
||||
"Prefix: %{_prefix}",
|
||||
f"BuildArch: {platform.machine()}",
|
||||
]
|
||||
|
||||
# Fix for conda
|
||||
if IS_CONDA:
|
||||
spec_file.append("%define debug_package %{nil}")
|
||||
|
||||
# Workaround for #14443 which affects some RPM based systems such as
|
||||
# RHEL6 (and probably derivatives)
|
||||
vendor_hook = check_output(
|
||||
[self._rpm, "--eval", "%{__os_install_post}"], text=True
|
||||
)
|
||||
# Generate a potential replacement value for __os_install_post (whilst
|
||||
# normalizing the whitespace to simplify the test for whether the
|
||||
# invocation of brp-python-bytecompile passes in __python):
|
||||
vendor_hook = "\n".join(
|
||||
[f" {line.strip()} \\" for line in vendor_hook.splitlines()]
|
||||
)
|
||||
problem = "brp-python-bytecompile \\\n"
|
||||
fixed = "brp-python-bytecompile %{__python} \\\n"
|
||||
fixed_hook = vendor_hook.replace(problem, fixed)
|
||||
if fixed_hook != vendor_hook:
|
||||
spec_file += [
|
||||
"# Workaround for http://bugs.python.org/issue14443",
|
||||
f"%define __python {sys.executable}",
|
||||
f"%define __os_install_post {fixed_hook}",
|
||||
"",
|
||||
]
|
||||
|
||||
# we create the spec file before running 'tar' in case of --spec-only.
|
||||
spec_file.append("Source0: %{name}-%{unmangled_version}.tar.gz")
|
||||
|
||||
for field in (
|
||||
"Vendor",
|
||||
"Packager",
|
||||
"Provides",
|
||||
"Requires",
|
||||
"Conflicts",
|
||||
"Obsoletes",
|
||||
):
|
||||
val = getattr(self, field.lower())
|
||||
if isinstance(val, list):
|
||||
join_val = " ".join(val)
|
||||
spec_file.append(f"{field}: {join_val}")
|
||||
elif val is not None:
|
||||
spec_file.append(f"{field}: {val}")
|
||||
|
||||
if dist.get_url() not in (None, "UNKNOWN"):
|
||||
spec_file.append(f"Url: {dist.get_url()}")
|
||||
|
||||
if self.distribution_name:
|
||||
spec_file.append(f"Distribution: {self.distribution_name}")
|
||||
|
||||
if self.build_requires:
|
||||
spec_file.append("BuildRequires: " + " ".join(self.build_requires))
|
||||
|
||||
if self.icon:
|
||||
spec_file.append("Icon: " + os.path.basename(self.icon))
|
||||
|
||||
if self.no_autoreq:
|
||||
spec_file.append("AutoReq: 0")
|
||||
|
||||
spec_file += [
|
||||
"",
|
||||
"%description",
|
||||
dist.get_long_description() or dist.get_description() or "UNKNOWN",
|
||||
]
|
||||
|
||||
# rpm scripts - figure out default build script
|
||||
if dist.script_name == "cxfreeze":
|
||||
def_setup_call = shutil.which(dist.script_name)
|
||||
else:
|
||||
def_setup_call = f"{sys.executable} {dist.script_name}"
|
||||
def_build = f"{def_setup_call} build_exe --optimize=1 --silent"
|
||||
def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build
|
||||
|
||||
# insert contents of files
|
||||
|
||||
# this is kind of misleading: user-supplied options are files
|
||||
# that we open and interpolate into the spec file, but the defaults
|
||||
# are just text that we drop in as-is.
|
||||
|
||||
install_cmd = (
|
||||
f"{def_setup_call} install --skip-build"
|
||||
" --prefix=%{_prefix} --root=%{buildroot}"
|
||||
)
|
||||
|
||||
script_options = [
|
||||
("prep", "prep_script", "%setup -n %{name}-%{unmangled_version}"),
|
||||
("build", "build_script", def_build),
|
||||
("install", "install_script", install_cmd),
|
||||
("clean", "clean_script", "rm -rf %{buildroot}"),
|
||||
("verifyscript", "verify_script", None),
|
||||
("pre", "pre_install", None),
|
||||
("post", "post_install", None),
|
||||
("preun", "pre_uninstall", None),
|
||||
("postun", "post_uninstall", None),
|
||||
]
|
||||
|
||||
for rpm_opt, attr, default in script_options:
|
||||
# Insert contents of file referred to, if no file is referred to
|
||||
# use 'default' as contents of script
|
||||
val = getattr(self, attr)
|
||||
if val or default:
|
||||
spec_file.extend(["", "%" + rpm_opt])
|
||||
if val:
|
||||
with open(val, encoding="utf_8") as file:
|
||||
spec_file.extend(file.read().split("\n"))
|
||||
else:
|
||||
spec_file.append(default)
|
||||
|
||||
# files section
|
||||
spec_file += [
|
||||
"",
|
||||
"%files",
|
||||
"%dir %{_prefix}/lib/%{name}-%{unmangled_version}",
|
||||
"%{_prefix}/lib/%{name}-%{unmangled_version}/*",
|
||||
"%{_bindir}/%{name}",
|
||||
"%defattr(-,root,root)",
|
||||
]
|
||||
|
||||
if self.doc_files:
|
||||
spec_file.append("%doc " + " ".join(self.doc_files))
|
||||
|
||||
if self.changelog:
|
||||
spec_file.extend(["", "%changelog"])
|
||||
spec_file.extend(self.changelog)
|
||||
|
||||
return spec_file
|
||||
|
||||
@staticmethod
|
||||
def _format_changelog(changelog) -> list[str]:
|
||||
"""Format the changelog correctly and convert it to a string list."""
|
||||
if not changelog:
|
||||
return changelog
|
||||
new_changelog = []
|
||||
for raw_line in changelog.strip().split("\n"):
|
||||
line = raw_line.strip()
|
||||
if line[0] == "*":
|
||||
new_changelog.extend(["", line])
|
||||
elif line[0] == "-":
|
||||
new_changelog.append(line)
|
||||
else:
|
||||
new_changelog.append(" " + line)
|
||||
|
||||
# strip trailing newline inserted by first changelog entry
|
||||
if not new_changelog[0]:
|
||||
del new_changelog[0]
|
||||
|
||||
return new_changelog
|
||||
|
||||
|
||||
def write_file(filename, contents) -> None:
|
||||
"""Create a file with the specified name and write 'contents'
|
||||
(a sequence of strings without line terminators) to it.
|
||||
"""
|
||||
with open(filename, "w", encoding="utf_8") as file:
|
||||
for line in contents:
|
||||
file.write(line + "\n")
|
||||
321
venv3_12/Lib/site-packages/cx_Freeze/command/build_exe.py
Normal file
321
venv3_12/Lib/site-packages/cx_Freeze/command/build_exe.py
Normal file
@@ -0,0 +1,321 @@
|
||||
"""Implements the 'build_exe' command."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from sysconfig import get_platform, get_python_version
|
||||
from typing import ClassVar
|
||||
|
||||
from setuptools import Command
|
||||
|
||||
from cx_Freeze._compat import IS_WINDOWS
|
||||
from cx_Freeze.common import normalize_to_list
|
||||
from cx_Freeze.exception import OptionError, SetupError
|
||||
from cx_Freeze.freezer import Freezer
|
||||
from cx_Freeze.module import ConstantsModule
|
||||
|
||||
__all__ = ["build_exe"]
|
||||
|
||||
|
||||
class build_exe(Command):
|
||||
"""Build executables from Python scripts."""
|
||||
|
||||
description = "build executables from Python scripts"
|
||||
user_options: ClassVar[list[tuple[str, str | None, str]]] = [
|
||||
(
|
||||
"build-exe=",
|
||||
"b",
|
||||
"directory for built executables and dependent files",
|
||||
),
|
||||
("includes=", "i", "comma-separated list of modules to include"),
|
||||
("excludes=", "e", "comma-separated list of modules to exclude"),
|
||||
(
|
||||
"packages=",
|
||||
"p",
|
||||
"comma-separated list of packages to include, "
|
||||
"which includes all submodules in the package",
|
||||
),
|
||||
(
|
||||
"replace-paths=",
|
||||
None,
|
||||
"comma-separated list of paths to replace in included modules, "
|
||||
"using the form <search>=<replace>",
|
||||
),
|
||||
(
|
||||
"path=",
|
||||
None,
|
||||
"comma-separated list of paths to search for modules; the default "
|
||||
"value is sys.path (use only if you know what you are doing)",
|
||||
),
|
||||
(
|
||||
"include-path=",
|
||||
None,
|
||||
"comma-separated list of paths to modify the search for modules",
|
||||
),
|
||||
("constants=", None, "comma-separated list of constants to include"),
|
||||
(
|
||||
"bin-includes=",
|
||||
None,
|
||||
"list of files to include when determining "
|
||||
"dependencies of binary files that would normally be excluded",
|
||||
),
|
||||
(
|
||||
"bin-excludes=",
|
||||
None,
|
||||
"list of files to exclude when determining "
|
||||
"dependencies of binary files that would normally be included",
|
||||
),
|
||||
(
|
||||
"bin-path-includes=",
|
||||
None,
|
||||
"list of paths from which to include files when determining "
|
||||
"dependencies of binary files",
|
||||
),
|
||||
(
|
||||
"bin-path-excludes=",
|
||||
None,
|
||||
"list of paths from which to exclude files when determining "
|
||||
"dependencies of binary files",
|
||||
),
|
||||
(
|
||||
"include-files=",
|
||||
"f",
|
||||
"list of tuples of additional files to include in distribution",
|
||||
),
|
||||
(
|
||||
"zip-includes=",
|
||||
None,
|
||||
"list of tuples of additional files to include in zip file",
|
||||
),
|
||||
(
|
||||
"zip-include-packages=",
|
||||
None,
|
||||
"comma-separated list of packages to include in the zip file "
|
||||
"(or * for all) [default: none]",
|
||||
),
|
||||
(
|
||||
"zip-exclude-packages=",
|
||||
None,
|
||||
"comma-separated list of packages to exclude from the zip file "
|
||||
"and place in the file system instead (or * for all) "
|
||||
"[default: *]",
|
||||
),
|
||||
(
|
||||
"zip-filename=",
|
||||
None,
|
||||
"filename for the shared zipfile (.zip) "
|
||||
'[default: "library.zip" or None if --no-compress is used]',
|
||||
),
|
||||
(
|
||||
"no-compress",
|
||||
None,
|
||||
"create a zip file with no compression (See also --zip-filename)",
|
||||
),
|
||||
(
|
||||
"optimize=",
|
||||
"O",
|
||||
'optimization level: -O1 for "python -O", '
|
||||
'-O2 for "python -OO" and -O0 to disable [default: -O0]',
|
||||
),
|
||||
(
|
||||
"silent",
|
||||
"s",
|
||||
"suppress all output except warnings "
|
||||
"(equivalent to --silent-level=1)",
|
||||
),
|
||||
(
|
||||
"silent-level=",
|
||||
None,
|
||||
"suppress output from build_exe command."
|
||||
" level 0: get all messages; [default]"
|
||||
" level 1: suppress information messages, but still get warnings;"
|
||||
" (equivalent to --silent)"
|
||||
" level 2: suppress missing missing-module warnings"
|
||||
" level 3: suppress all warning messages",
|
||||
),
|
||||
(
|
||||
"include-msvcr",
|
||||
None,
|
||||
"include the Microsoft Visual C runtime files",
|
||||
),
|
||||
]
|
||||
boolean_options: ClassVar[list[str]] = [
|
||||
"no-compress",
|
||||
"include-msvcr",
|
||||
"silent",
|
||||
]
|
||||
|
||||
def add_to_path(self, name) -> None:
|
||||
source_dir = getattr(self, name.lower())
|
||||
if source_dir is not None:
|
||||
sys.path.insert(0, source_dir)
|
||||
|
||||
def build_extension(self, name, module_name=None) -> str | None:
|
||||
# XXX: This method, add_to_path and set_source_location can be deleted?
|
||||
if module_name is None:
|
||||
module_name = name
|
||||
source_dir = getattr(self, name.lower())
|
||||
if source_dir is None:
|
||||
return None
|
||||
orig_dir = os.getcwd()
|
||||
script_args = ["build"]
|
||||
command = self.distribution.get_command_obj("build")
|
||||
if command.compiler is not None:
|
||||
script_args.append(f"--compiler={command.compiler}")
|
||||
os.chdir(source_dir)
|
||||
logging.info("building '%s' extension in '%s'", name, source_dir)
|
||||
distutils_core = __import__("distutils.core", fromlist=["run_setup"])
|
||||
distribution = distutils_core.run_setup("setup.py", script_args)
|
||||
ext_modules = distribution.ext_modules
|
||||
modules = [m for m in ext_modules if m.name == module_name]
|
||||
if not modules:
|
||||
msg = f"no module named '{module_name}' in '{source_dir}'"
|
||||
raise SetupError(msg)
|
||||
command = distribution.get_command_obj("build_ext")
|
||||
command.ensure_finalized()
|
||||
if command.compiler is None:
|
||||
command.run()
|
||||
else:
|
||||
command.build_extensions()
|
||||
dir_name = os.path.join(source_dir, command.build_lib)
|
||||
os.chdir(orig_dir)
|
||||
if dir_name not in sys.path:
|
||||
sys.path.insert(0, dir_name)
|
||||
return os.path.join(
|
||||
source_dir,
|
||||
command.build_lib,
|
||||
command.get_ext_filename(module_name),
|
||||
)
|
||||
|
||||
def initialize_options(self) -> None:
|
||||
self.list_options = [
|
||||
"excludes",
|
||||
"includes",
|
||||
"packages",
|
||||
"replace_paths",
|
||||
"constants",
|
||||
"include_files",
|
||||
"include_path",
|
||||
"bin_excludes",
|
||||
"bin_includes",
|
||||
"bin_path_excludes",
|
||||
"bin_path_includes",
|
||||
"zip_includes",
|
||||
"zip_exclude_packages",
|
||||
"zip_include_packages",
|
||||
]
|
||||
for option in self.list_options:
|
||||
setattr(self, option, [])
|
||||
self.zip_exclude_packages = ["*"]
|
||||
|
||||
self.build_exe = None
|
||||
self.include_msvcr = None
|
||||
self.no_compress = False
|
||||
self.optimize = 0
|
||||
self.path = None
|
||||
self.silent = None
|
||||
self.silent_level = None
|
||||
self.zip_filename = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
build = self.get_finalized_command("build")
|
||||
# check use of deprecated option
|
||||
options = build.distribution.get_option_dict("build")
|
||||
if options.get("build_exe", (None, None)) != (None, None):
|
||||
msg = (
|
||||
"[REMOVED] The use of build command with 'build-exe' "
|
||||
"option is deprecated.\n\t\t"
|
||||
"Use build_exe command with 'build-exe' option instead."
|
||||
)
|
||||
raise OptionError(msg)
|
||||
# check values of build_base and build_exe
|
||||
self.build_base = build.build_base
|
||||
if self.build_exe == self.build_base:
|
||||
msg = "build_exe option cannot be the same as build_base directory"
|
||||
raise SetupError(msg)
|
||||
if not self.build_exe: # empty or None
|
||||
dir_name = f"exe.{get_platform()}-{get_python_version()}"
|
||||
self.build_exe = os.path.join(self.build_base, dir_name)
|
||||
|
||||
# make sure all options of multiple values are lists
|
||||
for option in self.list_options:
|
||||
setattr(self, option, normalize_to_list(getattr(self, option)))
|
||||
|
||||
# path - accepts os.pathsep to be backwards compatible with CLI
|
||||
if self.path and isinstance(self.path, str):
|
||||
self.path = self.path.replace(os.pathsep, ",")
|
||||
include_path = self.include_path
|
||||
if include_path:
|
||||
self.path = include_path + normalize_to_list(self.path or sys.path)
|
||||
|
||||
# the degree of silencing, set from either the silent or silent-level
|
||||
# option, as appropriate
|
||||
self.silent = int(self.silent or self.silent_level or 0)
|
||||
|
||||
# compression options
|
||||
self.no_compress = bool(self.no_compress)
|
||||
if self.zip_filename:
|
||||
self.zip_filename = os.path.basename(
|
||||
os.path.splitext(self.zip_filename)[0] + ".zip"
|
||||
)
|
||||
elif self.no_compress is False:
|
||||
self.zip_filename = "library.zip"
|
||||
|
||||
# include-msvcr is used on Windows, but not in MingW
|
||||
self.include_msvcr = IS_WINDOWS and bool(self.include_msvcr)
|
||||
|
||||
# optimization level: 0,1,2
|
||||
self.optimize = int(self.optimize or 0)
|
||||
|
||||
def run(self) -> None:
|
||||
metadata = self.distribution.metadata
|
||||
constants_module = ConstantsModule(
|
||||
metadata.version, constants=self.constants
|
||||
)
|
||||
|
||||
freezer: Freezer = Freezer(
|
||||
self.distribution.executables,
|
||||
constants_module,
|
||||
self.includes,
|
||||
self.excludes,
|
||||
self.packages,
|
||||
self.replace_paths,
|
||||
(not self.no_compress),
|
||||
self.optimize,
|
||||
self.path,
|
||||
self.build_exe,
|
||||
bin_includes=self.bin_includes,
|
||||
bin_excludes=self.bin_excludes,
|
||||
bin_path_includes=self.bin_path_includes,
|
||||
bin_path_excludes=self.bin_path_excludes,
|
||||
include_files=self.include_files,
|
||||
zip_includes=self.zip_includes,
|
||||
zip_include_packages=self.zip_include_packages,
|
||||
zip_exclude_packages=self.zip_exclude_packages,
|
||||
silent=self.silent,
|
||||
metadata=metadata,
|
||||
include_msvcr=self.include_msvcr,
|
||||
zip_filename=self.zip_filename,
|
||||
)
|
||||
|
||||
freezer.freeze()
|
||||
freezer.print_report()
|
||||
|
||||
def set_source_location(self, name, *pathParts) -> None:
|
||||
env_name = f"{name.upper()}_BASE"
|
||||
attr_name = name.lower()
|
||||
source_dir = getattr(self, attr_name)
|
||||
if source_dir is None:
|
||||
base_dir = os.environ.get(env_name)
|
||||
if base_dir is None:
|
||||
return
|
||||
source_dir = os.path.join(base_dir, *pathParts)
|
||||
if os.path.isdir(source_dir):
|
||||
setattr(self, attr_name, source_dir)
|
||||
|
||||
# -- Predicates for the sub-command list ---------------------------
|
||||
|
||||
def has_executables(self) -> bool:
|
||||
return getattr(self.distribution, "executables", None) is not None
|
||||
78
venv3_12/Lib/site-packages/cx_Freeze/command/install.py
Normal file
78
venv3_12/Lib/site-packages/cx_Freeze/command/install.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Extends setuptools 'install' command."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from typing import ClassVar, ContextManager
|
||||
|
||||
from setuptools.command.install import install as _install
|
||||
|
||||
__all__ = ["Install"]
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def suppress_known_deprecation() -> ContextManager:
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings("ignore", "setup.py install is deprecated")
|
||||
yield
|
||||
|
||||
|
||||
class Install(_install):
|
||||
"""Install everything from build directory."""
|
||||
|
||||
command_name = "install"
|
||||
user_options: ClassVar[list[tuple[str, str | None, str]]] = [
|
||||
*_install.user_options,
|
||||
("install-exe=", None, "installation directory for executables"),
|
||||
]
|
||||
|
||||
def expand_dirs(self) -> None:
|
||||
super().expand_dirs()
|
||||
self._expand_attrs(["install_exe"])
|
||||
|
||||
def get_sub_commands(self) -> list[str]:
|
||||
sub_commands = super().get_sub_commands()[:]
|
||||
if self.distribution.executables:
|
||||
sub_commands.remove("install_egg_info")
|
||||
sub_commands.remove("install_scripts")
|
||||
sub_commands.append("install_exe")
|
||||
return sub_commands
|
||||
|
||||
def initialize_options(self) -> None:
|
||||
with suppress_known_deprecation():
|
||||
super().initialize_options()
|
||||
self.install_exe = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
if self.prefix is None and sys.platform == "win32":
|
||||
winreg = __import__("winreg")
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"Software\Microsoft\Windows\CurrentVersion",
|
||||
)
|
||||
base = winreg.QueryValueEx(key, "ProgramFilesDir")[0]
|
||||
metadata = self.distribution.metadata
|
||||
self.prefix = os.path.join(
|
||||
os.path.normpath(base), metadata.get_name()
|
||||
)
|
||||
super().finalize_options()
|
||||
self.convert_paths("exe")
|
||||
if self.root is not None:
|
||||
self.change_roots("exe")
|
||||
|
||||
def select_scheme(self, name) -> None:
|
||||
super().select_scheme(name)
|
||||
if self.install_exe is None:
|
||||
if sys.platform == "win32":
|
||||
self.install_exe = "$base"
|
||||
else:
|
||||
metadata = self.distribution.metadata
|
||||
dir_name = f"{metadata.get_name()}-{metadata.get_version()}"
|
||||
self.install_exe = f"$base/lib/{dir_name}"
|
||||
|
||||
def run(self) -> None:
|
||||
# setuptools used inspect.currentframe(), this method needs to exist.
|
||||
super().run()
|
||||
76
venv3_12/Lib/site-packages/cx_Freeze/command/install_exe.py
Normal file
76
venv3_12/Lib/site-packages/cx_Freeze/command/install_exe.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""Implements the 'install_exe' command."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from typing import ClassVar
|
||||
|
||||
from setuptools import Command
|
||||
|
||||
__all__ = ["install_exe"]
|
||||
|
||||
|
||||
class install_exe(Command):
|
||||
"""Install executables built from Python scripts."""
|
||||
|
||||
command_name = "install_exe"
|
||||
description = "install executables built from Python scripts"
|
||||
user_options: ClassVar[list[tuple[str, str | None, str]]] = [
|
||||
("install-dir=", "d", "directory to install executables to"),
|
||||
("build-dir=", "b", "build directory (where to install from)"),
|
||||
("force", "f", "force installation (overwrite existing files)"),
|
||||
("skip-build", None, "skip the build steps"),
|
||||
]
|
||||
|
||||
def initialize_options(self) -> None:
|
||||
self.install_dir: str | None = None
|
||||
self.force = 0
|
||||
self.build_dir = None
|
||||
self.skip_build = None
|
||||
self.outfiles = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
self.set_undefined_options("build_exe", ("build_exe", "build_dir"))
|
||||
self.set_undefined_options(
|
||||
"install",
|
||||
("install_exe", "install_dir"),
|
||||
("force", "force"),
|
||||
("skip_build", "skip_build"),
|
||||
)
|
||||
|
||||
def run(self) -> None:
|
||||
if not self.skip_build:
|
||||
self.run_command("build_exe")
|
||||
|
||||
self.mkpath(self.install_dir)
|
||||
self.outfiles = self.copy_tree(self.build_dir, self.install_dir)
|
||||
|
||||
if sys.platform == "win32":
|
||||
return
|
||||
|
||||
# in posix, make symlinks to the executables
|
||||
install_dir = self.install_dir
|
||||
bin_dir = os.path.join(
|
||||
os.path.dirname(os.path.dirname(install_dir)), "bin"
|
||||
)
|
||||
self.execute(shutil.rmtree, (bin_dir, True), msg=f"removing {bin_dir}")
|
||||
self.mkpath(bin_dir)
|
||||
for executable in self.get_inputs():
|
||||
name = executable.target_name
|
||||
target = os.path.join(install_dir, name)
|
||||
origin = os.path.join(bin_dir, name)
|
||||
relative_reference = os.path.relpath(target, bin_dir)
|
||||
self.execute(
|
||||
os.symlink,
|
||||
(relative_reference, origin, True),
|
||||
msg=f"linking {origin} -> {relative_reference}",
|
||||
)
|
||||
self.outfiles.append(origin)
|
||||
|
||||
def get_inputs(self) -> list[str]:
|
||||
return self.distribution.executables or []
|
||||
|
||||
def get_outputs(self) -> list[str]:
|
||||
return self.outfiles or []
|
||||
Reference in New Issue
Block a user