256 lines
8.3 KiB
Python
256 lines
8.3 KiB
Python
"""cxfreeze command line tool."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from cx_Freeze import __version__, setup
|
|
from cx_Freeze._pyproject import get_pyproject_tool_data
|
|
|
|
__all__ = ["main"]
|
|
|
|
DESCRIPTION = """
|
|
Freeze a Python script and all of its referenced modules to a base \
|
|
executable which can then be distributed without requiring a Python \
|
|
installation.
|
|
"""
|
|
|
|
VERSION = f"""
|
|
%(prog)s {__version__}
|
|
Copyright (c) 2020-2024 Marcelo Duarte. All rights reserved.
|
|
Copyright (c) 2007-2019 Anthony Tuininga. All rights reserved.
|
|
Copyright (c) 2001-2006 Computronix Corporation. All rights reserved.
|
|
"""
|
|
|
|
EPILOG = """
|
|
Note:
|
|
* Windows only options are ignored by other OS and \
|
|
when used by Python app from Microsoft Store.
|
|
|
|
Additional help:
|
|
%(prog)s build_exe --help
|
|
|
|
Linux and similar OS:
|
|
%(prog)s bdist_appimage --help
|
|
%(prog)s bdist_deb --help
|
|
%(prog)s bdist_rpm --help
|
|
macOS:
|
|
%(prog)s bdist_dmg --help
|
|
%(prog)s bdist_mac --help
|
|
Windows:
|
|
%(prog)s bdist_msi --help
|
|
"""
|
|
|
|
|
|
def prepare_parser() -> argparse.ArgumentParser:
|
|
"""Helper function to parse the arguments."""
|
|
parser = argparse.ArgumentParser(
|
|
prog="cxfreeze",
|
|
description=DESCRIPTION,
|
|
epilog=EPILOG,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
add_help=False,
|
|
)
|
|
# Executable parameters
|
|
parser.add_argument(
|
|
"--script",
|
|
metavar="NAME",
|
|
help="the name of the file containing the script which is to be "
|
|
"frozen",
|
|
)
|
|
parser.add_argument(
|
|
"--init-script",
|
|
metavar="NAME",
|
|
help="script which will be executed upon startup; if the name of the "
|
|
"file is not an absolute file name, the subdirectory initscripts "
|
|
"(rooted in the directory in which the cx_Freeze package is found) "
|
|
"will be searched for a file matching the name",
|
|
)
|
|
parser.add_argument(
|
|
"--base",
|
|
"--base-name",
|
|
metavar="NAME",
|
|
help="the name of the base executable; the pre-defined values are: "
|
|
'"console", "gui" and "service"; a user-defined base is accepted '
|
|
"if it is given with an absolute path name [default: console]",
|
|
)
|
|
parser.add_argument(
|
|
"--target-name",
|
|
metavar="NAME",
|
|
help="the name of the target executable; the default value is the "
|
|
"name of the script; it is recommended NOT to use an extension "
|
|
"(automatically added on Windows); target-name with version is "
|
|
"supported; if specified a path, raise an error",
|
|
)
|
|
parser.add_argument(
|
|
"--target-dir",
|
|
metavar="DIR",
|
|
help="directory for built executables and dependent files",
|
|
)
|
|
parser.add_argument(
|
|
"--icon",
|
|
metavar="NAME",
|
|
help="name of icon which should be included in the executable itself "
|
|
"on Windows or placed in the target directory for other platforms; "
|
|
"it is recommended NOT to use an extension (automatically added "
|
|
'".ico" on Windows, ".icns" on macOS and ".png" or ".svg" on Linux '
|
|
"and others)",
|
|
)
|
|
parser.add_argument(
|
|
"--manifest",
|
|
metavar="NAME",
|
|
help="name of manifest which should be included in the executable "
|
|
"itself (Windows only)",
|
|
)
|
|
parser.add_argument(
|
|
"--uac-admin",
|
|
action="store_true",
|
|
help="creates a manifest for an application that will request "
|
|
"elevation (Windows only)",
|
|
)
|
|
parser.add_argument(
|
|
"--uac-uiaccess",
|
|
action="store_true",
|
|
dest="uac_uiaccess",
|
|
help="changes the application manifest to bypass user interface "
|
|
"control (Windows only)",
|
|
)
|
|
parser.add_argument(
|
|
"--shortcut-name",
|
|
metavar="NAME",
|
|
help="the name to give a shortcut for the executable when included in "
|
|
"an MSI package (Windows only)",
|
|
)
|
|
parser.add_argument(
|
|
"--shortcut-dir",
|
|
metavar="DIR",
|
|
help="the directory in which to place the shortcut when being instal"
|
|
"led by an MSI package; see the MSI Shortcut table documentation for "
|
|
"more information on what values can be placed here (Windows only)",
|
|
)
|
|
parser.add_argument(
|
|
"--copyright",
|
|
help="the copyright value to include in the version resource "
|
|
"associated with executable (Windows only)",
|
|
)
|
|
parser.add_argument(
|
|
"--trademarks",
|
|
help="the trademarks value to include in the version resource "
|
|
"associated with the executable (Windows only)",
|
|
)
|
|
# Command positional parameter
|
|
parser.add_argument(
|
|
"command",
|
|
nargs=argparse.OPTIONAL,
|
|
metavar="COMMAND",
|
|
help="build, build_exe or supported bdist commands (and to be "
|
|
"backwards compatible, can replace --script option)",
|
|
)
|
|
# Version
|
|
parser.add_argument("--version", action="version", version=VERSION)
|
|
|
|
return parser
|
|
|
|
|
|
def main() -> None:
|
|
"""Entry point for cxfreeze command line tool."""
|
|
sys.setrecursionlimit(sys.getrecursionlimit() * 10)
|
|
|
|
parser = prepare_parser()
|
|
args, argv = parser.parse_known_args()
|
|
script = args.script
|
|
command = args.command
|
|
|
|
# help
|
|
if "-h" in argv or "--help" in argv:
|
|
if command is None:
|
|
parser.print_help()
|
|
else:
|
|
setup(
|
|
executables=None,
|
|
script_args=[command, "--help"],
|
|
script_name=parser.prog,
|
|
)
|
|
parser.exit()
|
|
|
|
# usage
|
|
deprecated = []
|
|
if script is None:
|
|
if command is None:
|
|
parser.error("--script or command must be specified")
|
|
elif not command.startswith(("build", "bdist", "install")):
|
|
args.script, command = command, script # backwards compatible
|
|
deprecated.append("usage: required to use --script NAME")
|
|
if command is None:
|
|
command = "build_exe"
|
|
|
|
# deprecated options
|
|
if command == "build_exe" or "build_exe" in argv:
|
|
args_to_replace = [
|
|
("--install-dir", "--build-exe"),
|
|
("--exclude-modules", "--excludes"),
|
|
("--include-modules", "--includes"),
|
|
("-c", None),
|
|
("--compress", None),
|
|
("-OO", "--optimize=2"), # test -OO before -O
|
|
("-O", "--optimize=1"),
|
|
("-z", "--zip-includes"),
|
|
("--default-path", "--path"),
|
|
("-s", "--silent"),
|
|
]
|
|
new_argv = []
|
|
for arg in argv:
|
|
new_argv.append(arg)
|
|
for search, replace in args_to_replace:
|
|
if arg.startswith(search):
|
|
new_argv.pop()
|
|
if replace is None:
|
|
deprecated.append(f"{search} option removed")
|
|
else:
|
|
new_argv.append(arg.replace(search, replace))
|
|
deprecated.append(
|
|
f"{search} option replaced by {replace}"
|
|
)
|
|
break
|
|
argv = new_argv
|
|
|
|
# redirected options
|
|
if args.target_dir:
|
|
argv.append(f"--build-exe={args.target_dir}")
|
|
delattr(args, "target_dir")
|
|
|
|
# finalize command line options
|
|
executables = []
|
|
script_args = [command, *argv]
|
|
if args.script:
|
|
delattr(args, "command")
|
|
executables = [vars(args)]
|
|
if script_args[0] == "build" and "build_exe" not in script_args:
|
|
script_args.insert(1, "build_exe")
|
|
|
|
# fix sys.path for cxfreeze command line
|
|
command = Path(sys.argv[0])
|
|
if command.stem == "cxfreeze":
|
|
path_to_remove = os.fspath(command.parent)
|
|
if path_to_remove in sys.path:
|
|
sys.path.remove(path_to_remove)
|
|
sys.path.insert(0, os.getcwd())
|
|
|
|
# get options from pyproject.toml
|
|
options = get_pyproject_tool_data()
|
|
executables.extend(options.pop("executables", []))
|
|
|
|
setup(
|
|
command_options=options,
|
|
executables=executables,
|
|
script_args=script_args,
|
|
script_name=parser.prog,
|
|
)
|
|
|
|
if deprecated:
|
|
for warning_msg in deprecated:
|
|
print("WARNING: deprecated", warning_msg)
|