Final exam version
This commit is contained in:
247
venv/bin/Activate.ps1
Normal file
247
venv/bin/Activate.ps1
Normal file
@@ -0,0 +1,247 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Activate a Python virtual environment for the current PowerShell session.
|
||||
|
||||
.Description
|
||||
Pushes the python executable for a virtual environment to the front of the
|
||||
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||
in a Python virtual environment. Makes use of the command line switches as
|
||||
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||
|
||||
.Parameter VenvDir
|
||||
Path to the directory that contains the virtual environment to activate. The
|
||||
default value for this is the parent of the directory that the Activate.ps1
|
||||
script is located within.
|
||||
|
||||
.Parameter Prompt
|
||||
The prompt prefix to display when this virtual environment is activated. By
|
||||
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||
|
||||
.Example
|
||||
Activate.ps1
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Verbose
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and shows extra information about the activation as it executes.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||
Activates the Python virtual environment located in the specified location.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Prompt "MyPython"
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and prefixes the current prompt with the specified string (surrounded in
|
||||
parentheses) while the virtual environment is active.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
Param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$VenvDir,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$Prompt
|
||||
)
|
||||
|
||||
<# Function declarations --------------------------------------------------- #>
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Remove all shell session elements added by the Activate script, including the
|
||||
addition of the virtual environment's Python executable from the beginning of
|
||||
the PATH variable.
|
||||
|
||||
.Parameter NonDestructive
|
||||
If present, do not remove this function from the global namespace for the
|
||||
session.
|
||||
|
||||
#>
|
||||
function global:deactivate ([switch]$NonDestructive) {
|
||||
# Revert to original values
|
||||
|
||||
# The prior prompt:
|
||||
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# The prior PYTHONHOME:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
# The prior PATH:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Just remove the VIRTUAL_ENV altogether:
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV
|
||||
}
|
||||
|
||||
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||
}
|
||||
|
||||
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||
}
|
||||
|
||||
# Leave deactivate function in the global namespace if requested:
|
||||
if (-not $NonDestructive) {
|
||||
Remove-Item -Path function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Description
|
||||
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||
given folder, and returns them in a map.
|
||||
|
||||
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||
then it is considered a `key = value` line. The left hand string is the key,
|
||||
the right hand is the value.
|
||||
|
||||
If the value starts with a `'` or a `"` then the first and last character is
|
||||
stripped from the value before being captured.
|
||||
|
||||
.Parameter ConfigDir
|
||||
Path to the directory that contains the `pyvenv.cfg` file.
|
||||
#>
|
||||
function Get-PyVenvConfig(
|
||||
[String]
|
||||
$ConfigDir
|
||||
) {
|
||||
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||
|
||||
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||
|
||||
# An empty map will be returned if no config file is found.
|
||||
$pyvenvConfig = @{ }
|
||||
|
||||
if ($pyvenvConfigPath) {
|
||||
|
||||
Write-Verbose "File exists, parse `key = value` lines"
|
||||
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||
|
||||
$pyvenvConfigContent | ForEach-Object {
|
||||
$keyval = $PSItem -split "\s*=\s*", 2
|
||||
if ($keyval[0] -and $keyval[1]) {
|
||||
$val = $keyval[1]
|
||||
|
||||
# Remove extraneous quotations around a string value.
|
||||
if ("'""".Contains($val.Substring(0, 1))) {
|
||||
$val = $val.Substring(1, $val.Length - 2)
|
||||
}
|
||||
|
||||
$pyvenvConfig[$keyval[0]] = $val
|
||||
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pyvenvConfig
|
||||
}
|
||||
|
||||
|
||||
<# Begin Activate script --------------------------------------------------- #>
|
||||
|
||||
# Determine the containing directory of this script
|
||||
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||
|
||||
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||
|
||||
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||
# First, get the location of the virtual environment, it might not be
|
||||
# VenvExecDir if specified on the command line.
|
||||
if ($VenvDir) {
|
||||
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||
Write-Verbose "VenvDir=$VenvDir"
|
||||
}
|
||||
|
||||
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||
# as `prompt`.
|
||||
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||
|
||||
# Next, set the prompt from the command line, or the config file, or
|
||||
# just use the name of the virtual environment folder.
|
||||
if ($Prompt) {
|
||||
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||
$Prompt = $pyvenvCfg['prompt'];
|
||||
}
|
||||
else {
|
||||
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Prompt = '$Prompt'"
|
||||
Write-Verbose "VenvDir='$VenvDir'"
|
||||
|
||||
# Deactivate any currently active virtual environment, but leave the
|
||||
# deactivate function in place.
|
||||
deactivate -nondestructive
|
||||
|
||||
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
|
||||
# Set the prompt to include the env name
|
||||
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||
|
||||
function global:prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path -Path Env:PYTHONHOME) {
|
||||
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
Remove-Item -Path Env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
||||
70
venv/bin/activate
Normal file
70
venv/bin/activate
Normal file
@@ -0,0 +1,70 @@
|
||||
# This file must be used with "source bin/activate" *from bash*
|
||||
# You cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# Call hash to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
hash -r 2> /dev/null
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
unset VIRTUAL_ENV_PROMPT
|
||||
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
# on Windows, a path can contain colons and backslashes and has to be converted:
|
||||
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
|
||||
# transform D:\path\to\venv to /d/path/to/venv on MSYS
|
||||
# and to /cygdrive/d/path/to/venv on Cygwin
|
||||
export VIRTUAL_ENV=$(cygpath '/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv')
|
||||
else
|
||||
# use the path as-is
|
||||
export VIRTUAL_ENV='/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv'
|
||||
fi
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/"bin":$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1='(venv) '"${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT='(venv) '
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# Call hash to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
hash -r 2> /dev/null
|
||||
27
venv/bin/activate.csh
Normal file
27
venv/bin/activate.csh
Normal file
@@ -0,0 +1,27 @@
|
||||
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||
# You cannot run it directly.
|
||||
|
||||
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||
|
||||
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV '/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv'
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH"
|
||||
setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
|
||||
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||
|
||||
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||
set prompt = '(venv) '"$prompt"
|
||||
setenv VIRTUAL_ENV_PROMPT '(venv) '
|
||||
endif
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
||||
69
venv/bin/activate.fish
Normal file
69
venv/bin/activate.fish
Normal file
@@ -0,0 +1,69 @@
|
||||
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
||||
# (https://fishshell.com/). You cannot run it directly.
|
||||
|
||||
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
||||
# reset old environment variables
|
||||
if test -n "$_OLD_VIRTUAL_PATH"
|
||||
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||
set -e _OLD_VIRTUAL_PATH
|
||||
end
|
||||
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
||||
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||
end
|
||||
|
||||
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||
# prevents error when using nested fish instances (Issue #93858)
|
||||
if functions -q _old_fish_prompt
|
||||
functions -e fish_prompt
|
||||
functions -c _old_fish_prompt fish_prompt
|
||||
functions -e _old_fish_prompt
|
||||
end
|
||||
end
|
||||
|
||||
set -e VIRTUAL_ENV
|
||||
set -e VIRTUAL_ENV_PROMPT
|
||||
if test "$argv[1]" != "nondestructive"
|
||||
# Self-destruct!
|
||||
functions -e deactivate
|
||||
end
|
||||
end
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
set -gx VIRTUAL_ENV '/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv'
|
||||
|
||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||
set -gx PATH "$VIRTUAL_ENV/"bin $PATH
|
||||
|
||||
# Unset PYTHONHOME if set.
|
||||
if set -q PYTHONHOME
|
||||
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||
set -e PYTHONHOME
|
||||
end
|
||||
|
||||
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
# fish uses a function instead of an env var to generate the prompt.
|
||||
|
||||
# Save the current fish_prompt function as the function _old_fish_prompt.
|
||||
functions -c fish_prompt _old_fish_prompt
|
||||
|
||||
# With the original prompt function renamed, we can override with our own.
|
||||
function fish_prompt
|
||||
# Save the return status of the last command.
|
||||
set -l old_status $status
|
||||
|
||||
# Output the venv prompt; color taken from the blue of the Python logo.
|
||||
printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal)
|
||||
|
||||
# Restore the return status of the previous command.
|
||||
echo "exit $old_status" | .
|
||||
# Output the original/"old" prompt.
|
||||
_old_fish_prompt
|
||||
end
|
||||
|
||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||
set -gx VIRTUAL_ENV_PROMPT '(venv) '
|
||||
end
|
||||
8
venv/bin/pip
Executable file
8
venv/bin/pip
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
8
venv/bin/pip3
Executable file
8
venv/bin/pip3
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
8
venv/bin/pip3.12
Executable file
8
venv/bin/pip3.12
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
8
venv/bin/pyi-archive_viewer
Executable file
8
venv/bin/pyi-archive_viewer
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from PyInstaller.utils.cliutils.archive_viewer import run
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(run())
|
||||
8
venv/bin/pyi-bindepend
Executable file
8
venv/bin/pyi-bindepend
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from PyInstaller.utils.cliutils.bindepend import run
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(run())
|
||||
8
venv/bin/pyi-grab_version
Executable file
8
venv/bin/pyi-grab_version
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from PyInstaller.utils.cliutils.grab_version import run
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(run())
|
||||
8
venv/bin/pyi-makespec
Executable file
8
venv/bin/pyi-makespec
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from PyInstaller.utils.cliutils.makespec import run
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(run())
|
||||
8
venv/bin/pyi-set_version
Executable file
8
venv/bin/pyi-set_version
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from PyInstaller.utils.cliutils.set_version import run
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(run())
|
||||
8
venv/bin/pyinstaller
Executable file
8
venv/bin/pyinstaller
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/egor/Загрузки/Final-main7/ExamFinal_never_collision/ExamFinal_safe_start_angles/venv/bin/python3.12
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from PyInstaller.__main__ import _console_script_run
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(_console_script_run())
|
||||
1
venv/bin/python
Symbolic link
1
venv/bin/python
Symbolic link
@@ -0,0 +1 @@
|
||||
python3.12
|
||||
1
venv/bin/python3
Symbolic link
1
venv/bin/python3
Symbolic link
@@ -0,0 +1 @@
|
||||
python3.12
|
||||
1
venv/bin/python3.12
Symbolic link
1
venv/bin/python3.12
Symbolic link
@@ -0,0 +1 @@
|
||||
/usr/bin/python3.12
|
||||
21
venv/include/site/python3.12/pygame/_blit_info.h
Normal file
21
venv/include/site/python3.12/pygame/_blit_info.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#define NO_PYGAME_C_API
|
||||
#include "_surface.h"
|
||||
|
||||
/* The structure passed to the low level blit functions */
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
Uint8 *s_pixels;
|
||||
int s_pxskip;
|
||||
int s_skip;
|
||||
Uint8 *d_pixels;
|
||||
int d_pxskip;
|
||||
int d_skip;
|
||||
SDL_PixelFormat *src;
|
||||
SDL_PixelFormat *dst;
|
||||
Uint8 src_blanket_alpha;
|
||||
int src_has_colorkey;
|
||||
Uint32 src_colorkey;
|
||||
SDL_BlendMode src_blend;
|
||||
SDL_BlendMode dst_blend;
|
||||
} SDL_BlitInfo;
|
||||
26
venv/include/site/python3.12/pygame/_camera.h
Normal file
26
venv/include/site/python3.12/pygame/_camera.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _CAMERA_H
|
||||
#define _CAMERA_H
|
||||
|
||||
#include "_pygame.h"
|
||||
#include "camera.h"
|
||||
|
||||
#endif
|
||||
374
venv/include/site/python3.12/pygame/_pygame.h
Normal file
374
venv/include/site/python3.12/pygame/_pygame.h
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
/* This will use PYGAMEAPI_EXTERN_SLOTS instead
|
||||
* of PYGAMEAPI_DEFINE_SLOTS for base modules.
|
||||
*/
|
||||
#ifndef _PYGAME_INTERNAL_H
|
||||
#define _PYGAME_INTERNAL_H
|
||||
|
||||
#include "pgplatform.h"
|
||||
/*
|
||||
If PY_SSIZE_T_CLEAN is defined before including Python.h, length is a
|
||||
Py_ssize_t rather than an int for all # variants of formats (s#, y#, etc.)
|
||||
*/
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
|
||||
/* Ensure PyPy-specific code is not in use when running on GraalPython (PR
|
||||
* #2580) */
|
||||
#if defined(GRAALVM_PYTHON) && defined(PYPY_VERSION)
|
||||
#undef PYPY_VERSION
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
/* SDL 1.2 constants removed from SDL 2 */
|
||||
typedef enum {
|
||||
SDL_HWSURFACE = 0,
|
||||
SDL_RESIZABLE = SDL_WINDOW_RESIZABLE,
|
||||
SDL_ASYNCBLIT = 0,
|
||||
SDL_OPENGL = SDL_WINDOW_OPENGL,
|
||||
SDL_OPENGLBLIT = 0,
|
||||
SDL_ANYFORMAT = 0,
|
||||
SDL_HWPALETTE = 0,
|
||||
SDL_DOUBLEBUF = 0,
|
||||
SDL_FULLSCREEN = SDL_WINDOW_FULLSCREEN,
|
||||
SDL_HWACCEL = 0,
|
||||
SDL_SRCCOLORKEY = 0,
|
||||
SDL_RLEACCELOK = 0,
|
||||
SDL_SRCALPHA = 0,
|
||||
SDL_NOFRAME = SDL_WINDOW_BORDERLESS,
|
||||
SDL_GL_SWAP_CONTROL = 0,
|
||||
TIMER_RESOLUTION = 0
|
||||
} PygameVideoFlags;
|
||||
|
||||
/* the wheel button constants were removed from SDL 2 */
|
||||
typedef enum {
|
||||
PGM_BUTTON_LEFT = SDL_BUTTON_LEFT,
|
||||
PGM_BUTTON_RIGHT = SDL_BUTTON_RIGHT,
|
||||
PGM_BUTTON_MIDDLE = SDL_BUTTON_MIDDLE,
|
||||
PGM_BUTTON_WHEELUP = 4,
|
||||
PGM_BUTTON_WHEELDOWN = 5,
|
||||
PGM_BUTTON_X1 = SDL_BUTTON_X1 + 2,
|
||||
PGM_BUTTON_X2 = SDL_BUTTON_X2 + 2,
|
||||
PGM_BUTTON_KEEP = 0x80
|
||||
} PygameMouseFlags;
|
||||
|
||||
typedef enum {
|
||||
/* Any SDL_* events here are for backward compatibility. */
|
||||
SDL_NOEVENT = 0,
|
||||
|
||||
SDL_ACTIVEEVENT = SDL_USEREVENT,
|
||||
SDL_VIDEORESIZE,
|
||||
SDL_VIDEOEXPOSE,
|
||||
|
||||
PGE_MIDIIN,
|
||||
PGE_MIDIOUT,
|
||||
PGE_KEYREPEAT, /* Special internal pygame event, for managing key-presses
|
||||
*/
|
||||
|
||||
/* DO NOT CHANGE THE ORDER OF EVENTS HERE */
|
||||
PGE_WINDOWSHOWN,
|
||||
PGE_WINDOWHIDDEN,
|
||||
PGE_WINDOWEXPOSED,
|
||||
PGE_WINDOWMOVED,
|
||||
PGE_WINDOWRESIZED,
|
||||
PGE_WINDOWSIZECHANGED,
|
||||
PGE_WINDOWMINIMIZED,
|
||||
PGE_WINDOWMAXIMIZED,
|
||||
PGE_WINDOWRESTORED,
|
||||
PGE_WINDOWENTER,
|
||||
PGE_WINDOWLEAVE,
|
||||
PGE_WINDOWFOCUSGAINED,
|
||||
PGE_WINDOWFOCUSLOST,
|
||||
PGE_WINDOWCLOSE,
|
||||
PGE_WINDOWTAKEFOCUS,
|
||||
PGE_WINDOWHITTEST,
|
||||
PGE_WINDOWICCPROFCHANGED,
|
||||
PGE_WINDOWDISPLAYCHANGED,
|
||||
|
||||
/* Here we define PGPOST_* events, events that act as a one-to-one
|
||||
* proxy for SDL events (and some extra events too!), the proxy is used
|
||||
* internally when pygame users use event.post()
|
||||
*
|
||||
* At a first glance, these may look redundant, but they are really
|
||||
* important, especially with event blocking. If proxy events are
|
||||
* not there, blocked events dont make it to our event filter, and
|
||||
* that can break a lot of stuff.
|
||||
*
|
||||
* IMPORTANT NOTE: Do not post events directly with these proxy types,
|
||||
* use the appropriate functions from event.c, that handle these proxy
|
||||
* events for you.
|
||||
* Proxy events are for internal use only */
|
||||
PGPOST_EVENTBEGIN, /* mark start of proxy-events */
|
||||
PGPOST_ACTIVEEVENT = PGPOST_EVENTBEGIN,
|
||||
PGPOST_APP_TERMINATING,
|
||||
PGPOST_APP_LOWMEMORY,
|
||||
PGPOST_APP_WILLENTERBACKGROUND,
|
||||
PGPOST_APP_DIDENTERBACKGROUND,
|
||||
PGPOST_APP_WILLENTERFOREGROUND,
|
||||
PGPOST_APP_DIDENTERFOREGROUND,
|
||||
PGPOST_AUDIODEVICEADDED,
|
||||
PGPOST_AUDIODEVICEREMOVED,
|
||||
PGPOST_CLIPBOARDUPDATE,
|
||||
PGPOST_CONTROLLERAXISMOTION,
|
||||
PGPOST_CONTROLLERBUTTONDOWN,
|
||||
PGPOST_CONTROLLERBUTTONUP,
|
||||
PGPOST_CONTROLLERDEVICEADDED,
|
||||
PGPOST_CONTROLLERDEVICEREMOVED,
|
||||
PGPOST_CONTROLLERDEVICEREMAPPED,
|
||||
PGPOST_CONTROLLERTOUCHPADDOWN,
|
||||
PGPOST_CONTROLLERTOUCHPADMOTION,
|
||||
PGPOST_CONTROLLERTOUCHPADUP,
|
||||
PGPOST_CONTROLLERSENSORUPDATE,
|
||||
PGPOST_DOLLARGESTURE,
|
||||
PGPOST_DOLLARRECORD,
|
||||
PGPOST_DROPFILE,
|
||||
PGPOST_DROPTEXT,
|
||||
PGPOST_DROPBEGIN,
|
||||
PGPOST_DROPCOMPLETE,
|
||||
PGPOST_FINGERMOTION,
|
||||
PGPOST_FINGERDOWN,
|
||||
PGPOST_FINGERUP,
|
||||
PGPOST_KEYDOWN,
|
||||
PGPOST_KEYMAPCHANGED,
|
||||
PGPOST_KEYUP,
|
||||
PGPOST_JOYAXISMOTION,
|
||||
PGPOST_JOYBALLMOTION,
|
||||
PGPOST_JOYHATMOTION,
|
||||
PGPOST_JOYBUTTONDOWN,
|
||||
PGPOST_JOYBUTTONUP,
|
||||
PGPOST_JOYDEVICEADDED,
|
||||
PGPOST_JOYDEVICEREMOVED,
|
||||
PGPOST_LOCALECHANGED,
|
||||
PGPOST_MIDIIN,
|
||||
PGPOST_MIDIOUT,
|
||||
PGPOST_MOUSEMOTION,
|
||||
PGPOST_MOUSEBUTTONDOWN,
|
||||
PGPOST_MOUSEBUTTONUP,
|
||||
PGPOST_MOUSEWHEEL,
|
||||
PGPOST_MULTIGESTURE,
|
||||
PGPOST_NOEVENT,
|
||||
PGPOST_QUIT,
|
||||
PGPOST_RENDER_TARGETS_RESET,
|
||||
PGPOST_RENDER_DEVICE_RESET,
|
||||
PGPOST_SYSWMEVENT,
|
||||
PGPOST_TEXTEDITING,
|
||||
PGPOST_TEXTINPUT,
|
||||
PGPOST_VIDEORESIZE,
|
||||
PGPOST_VIDEOEXPOSE,
|
||||
PGPOST_WINDOWSHOWN,
|
||||
PGPOST_WINDOWHIDDEN,
|
||||
PGPOST_WINDOWEXPOSED,
|
||||
PGPOST_WINDOWMOVED,
|
||||
PGPOST_WINDOWRESIZED,
|
||||
PGPOST_WINDOWSIZECHANGED,
|
||||
PGPOST_WINDOWMINIMIZED,
|
||||
PGPOST_WINDOWMAXIMIZED,
|
||||
PGPOST_WINDOWRESTORED,
|
||||
PGPOST_WINDOWENTER,
|
||||
PGPOST_WINDOWLEAVE,
|
||||
PGPOST_WINDOWFOCUSGAINED,
|
||||
PGPOST_WINDOWFOCUSLOST,
|
||||
PGPOST_WINDOWCLOSE,
|
||||
PGPOST_WINDOWTAKEFOCUS,
|
||||
PGPOST_WINDOWHITTEST,
|
||||
PGPOST_WINDOWICCPROFCHANGED,
|
||||
PGPOST_WINDOWDISPLAYCHANGED,
|
||||
|
||||
PGE_USEREVENT, /* this event must stay in this position only */
|
||||
|
||||
PG_NUMEVENTS =
|
||||
SDL_LASTEVENT /* Not an event. Indicates end of user events. */
|
||||
} PygameEventCode;
|
||||
|
||||
/* SDL1 ACTIVEEVENT state attribute can take the following values */
|
||||
/* These constant values are directly picked from SDL1 source */
|
||||
#define SDL_APPMOUSEFOCUS 0x01
|
||||
#define SDL_APPINPUTFOCUS 0x02
|
||||
#define SDL_APPACTIVE 0x04
|
||||
|
||||
/* Surface flags: based on SDL 1.2 flags */
|
||||
typedef enum {
|
||||
PGS_SWSURFACE = 0x00000000,
|
||||
PGS_HWSURFACE = 0x00000001,
|
||||
PGS_ASYNCBLIT = 0x00000004,
|
||||
|
||||
PGS_ANYFORMAT = 0x10000000,
|
||||
PGS_HWPALETTE = 0x20000000,
|
||||
PGS_DOUBLEBUF = 0x40000000,
|
||||
PGS_FULLSCREEN = 0x80000000,
|
||||
PGS_SCALED = 0x00000200,
|
||||
|
||||
PGS_OPENGL = 0x00000002,
|
||||
PGS_OPENGLBLIT = 0x0000000A,
|
||||
PGS_RESIZABLE = 0x00000010,
|
||||
PGS_NOFRAME = 0x00000020,
|
||||
PGS_SHOWN = 0x00000040, /* Added from SDL 2 */
|
||||
PGS_HIDDEN = 0x00000080, /* Added from SDL 2 */
|
||||
|
||||
PGS_HWACCEL = 0x00000100,
|
||||
PGS_SRCCOLORKEY = 0x00001000,
|
||||
PGS_RLEACCELOK = 0x00002000,
|
||||
PGS_RLEACCEL = 0x00004000,
|
||||
PGS_SRCALPHA = 0x00010000,
|
||||
PGS_PREALLOC = 0x01000000
|
||||
} PygameSurfaceFlags;
|
||||
|
||||
// TODO Implement check below in a way that does not break CI
|
||||
/* New buffer protocol (PEP 3118) implemented on all supported Py versions.
|
||||
#if !defined(Py_TPFLAGS_HAVE_NEWBUFFER)
|
||||
#error No support for PEP 3118/Py_TPFLAGS_HAVE_NEWBUFFER. Please use a
|
||||
supported Python version. #endif */
|
||||
|
||||
#define RAISE(x, y) (PyErr_SetString((x), (y)), NULL)
|
||||
#define DEL_ATTR_NOT_SUPPORTED_CHECK(name, value) \
|
||||
do { \
|
||||
if (!value) { \
|
||||
PyErr_Format(PyExc_AttributeError, "Cannot delete attribute %s", \
|
||||
name); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DEL_ATTR_NOT_SUPPORTED_CHECK_NO_NAME(value) \
|
||||
do { \
|
||||
if (!value) { \
|
||||
PyErr_SetString(PyExc_AttributeError, "Cannot delete attribute"); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Initialization checks
|
||||
*/
|
||||
|
||||
#define VIDEO_INIT_CHECK() \
|
||||
if (!SDL_WasInit(SDL_INIT_VIDEO)) \
|
||||
return RAISE(pgExc_SDLError, "video system not initialized")
|
||||
|
||||
#define JOYSTICK_INIT_CHECK() \
|
||||
if (!SDL_WasInit(SDL_INIT_JOYSTICK)) \
|
||||
return RAISE(pgExc_SDLError, "joystick system not initialized")
|
||||
|
||||
/* thread check */
|
||||
#ifdef WITH_THREAD
|
||||
#define PG_CHECK_THREADS() (1)
|
||||
#else /* ~WITH_THREAD */
|
||||
#define PG_CHECK_THREADS() \
|
||||
(RAISE(PyExc_NotImplementedError, "Python built without thread support"))
|
||||
#endif /* ~WITH_THREAD */
|
||||
|
||||
#define PyType_Init(x) (((x).ob_type) = &PyType_Type)
|
||||
|
||||
/* CPython 3.6 had initial and undocumented FASTCALL support, but we play it
|
||||
* safe by not relying on implementation details */
|
||||
#if PY_VERSION_HEX < 0x03070000
|
||||
|
||||
/* Macro for naming a pygame fastcall wrapper function */
|
||||
#define PG_FASTCALL_NAME(func) _##func##_fastcall_wrap
|
||||
|
||||
/* used to forward declare compat functions */
|
||||
#define PG_DECLARE_FASTCALL_FUNC(func, self_type) \
|
||||
static PyObject *PG_FASTCALL_NAME(func)(self_type * self, PyObject * args)
|
||||
|
||||
/* Using this macro on a function defined with the FASTCALL calling convention
|
||||
* adds a wrapper definition that uses regular python VARARGS convention.
|
||||
* Since it is guaranteed that the 'args' object is a tuple, we can directly
|
||||
* call PySequence_Fast_ITEMS and PyTuple_GET_SIZE on it (which are macros that
|
||||
* assume the same, and don't do error checking) */
|
||||
#define PG_WRAP_FASTCALL_FUNC(func, self_type) \
|
||||
PG_DECLARE_FASTCALL_FUNC(func, self_type) \
|
||||
{ \
|
||||
return func(self, (PyObject *const *)PySequence_Fast_ITEMS(args), \
|
||||
PyTuple_GET_SIZE(args)); \
|
||||
}
|
||||
|
||||
#define PG_FASTCALL METH_VARARGS
|
||||
|
||||
#else /* PY_VERSION_HEX >= 0x03070000 */
|
||||
/* compat macros are no-op on python versions that support fastcall */
|
||||
#define PG_FASTCALL_NAME(func) func
|
||||
#define PG_DECLARE_FASTCALL_FUNC(func, self_type)
|
||||
#define PG_WRAP_FASTCALL_FUNC(func, self_type)
|
||||
|
||||
#define PG_FASTCALL METH_FASTCALL
|
||||
|
||||
#endif /* PY_VERSION_HEX >= 0x03070000 */
|
||||
|
||||
/*
|
||||
* event module internals
|
||||
*/
|
||||
struct pgEventObject {
|
||||
PyObject_HEAD int type;
|
||||
PyObject *dict;
|
||||
};
|
||||
|
||||
/*
|
||||
* surflock module internals
|
||||
*/
|
||||
typedef struct {
|
||||
PyObject_HEAD PyObject *surface;
|
||||
PyObject *lockobj;
|
||||
PyObject *weakrefs;
|
||||
} pgLifetimeLockObject;
|
||||
|
||||
/*
|
||||
* surface module internals
|
||||
*/
|
||||
struct pgSubSurface_Data {
|
||||
PyObject *owner;
|
||||
int pixeloffset;
|
||||
int offsetx, offsety;
|
||||
};
|
||||
|
||||
/*
|
||||
* color module internals
|
||||
*/
|
||||
struct pgColorObject {
|
||||
PyObject_HEAD Uint8 data[4];
|
||||
Uint8 len;
|
||||
};
|
||||
|
||||
/*
|
||||
* include public API
|
||||
*/
|
||||
#include "include/_pygame.h"
|
||||
|
||||
/* Slot counts.
|
||||
* Remember to keep these constants up to date.
|
||||
*/
|
||||
|
||||
#define PYGAMEAPI_RECT_NUMSLOTS 5
|
||||
#define PYGAMEAPI_JOYSTICK_NUMSLOTS 2
|
||||
#define PYGAMEAPI_DISPLAY_NUMSLOTS 2
|
||||
#define PYGAMEAPI_SURFACE_NUMSLOTS 4
|
||||
#define PYGAMEAPI_SURFLOCK_NUMSLOTS 8
|
||||
#define PYGAMEAPI_RWOBJECT_NUMSLOTS 6
|
||||
#define PYGAMEAPI_PIXELARRAY_NUMSLOTS 2
|
||||
#define PYGAMEAPI_COLOR_NUMSLOTS 5
|
||||
#define PYGAMEAPI_MATH_NUMSLOTS 2
|
||||
#define PYGAMEAPI_BASE_NUMSLOTS 27
|
||||
#define PYGAMEAPI_EVENT_NUMSLOTS 6
|
||||
|
||||
#endif /* _PYGAME_INTERNAL_H */
|
||||
30
venv/include/site/python3.12/pygame/_surface.h
Normal file
30
venv/include/site/python3.12/pygame/_surface.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
Copyright (C) 2007 Marcus von Appen
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
#ifndef _SURFACE_H
|
||||
#define _SURFACE_H
|
||||
|
||||
#include "_pygame.h"
|
||||
#include "surface.h"
|
||||
|
||||
#endif
|
||||
218
venv/include/site/python3.12/pygame/camera.h
Normal file
218
venv/include/site/python3.12/pygame/camera.h
Normal file
@@ -0,0 +1,218 @@
|
||||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "pygame.h"
|
||||
#include "pgcompat.h"
|
||||
#include "doc/camera_doc.h"
|
||||
|
||||
#if defined(__unix__)
|
||||
#include <structmember.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <fcntl.h> /* low-level i/o */
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/* on freebsd there is no asm/types */
|
||||
#ifdef __linux__
|
||||
#include <asm/types.h> /* for videodev2.h */
|
||||
#include <linux/videodev2.h>
|
||||
#endif
|
||||
|
||||
/* on openbsd and netbsd we need to include videoio.h */
|
||||
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#include <sys/videoio.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <linux/videodev2.h>
|
||||
#endif
|
||||
|
||||
#endif /* defined(__unix__) */
|
||||
|
||||
#if defined(__WIN32__)
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#undef WINVER
|
||||
/** _WIN32_WINNT_WINBLUE sets minimum platform SDK to Windows 8.1. */
|
||||
#define WINVER _WIN32_WINNT_WINBLUE
|
||||
#endif
|
||||
|
||||
#include <mfapi.h>
|
||||
#include <mfobjects.h>
|
||||
#include <mfidl.h>
|
||||
#include <mfreadwrite.h>
|
||||
#include <combaseapi.h>
|
||||
#include <mftransform.h>
|
||||
#endif
|
||||
|
||||
/* some constants used which are not defined on non-v4l machines. */
|
||||
#ifndef V4L2_PIX_FMT_RGB24
|
||||
#define V4L2_PIX_FMT_RGB24 'RGB3'
|
||||
#endif
|
||||
#ifndef V4L2_PIX_FMT_RGB444
|
||||
#define V4L2_PIX_FMT_RGB444 'R444'
|
||||
#endif
|
||||
#ifndef V4L2_PIX_FMT_YUYV
|
||||
#define V4L2_PIX_FMT_YUYV 'YUYV'
|
||||
#endif
|
||||
#ifndef V4L2_PIX_FMT_XBGR32
|
||||
#define V4L2_PIX_FMT_XBGR32 'XR24'
|
||||
#endif
|
||||
|
||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
||||
#define SAT(c) \
|
||||
if (c & (~255)) { \
|
||||
if (c < 0) \
|
||||
c = 0; \
|
||||
else \
|
||||
c = 255; \
|
||||
}
|
||||
#define SAT2(c) ((c) & (~255) ? ((c) < 0 ? 0 : 255) : (c))
|
||||
#define DEFAULT_WIDTH 640
|
||||
#define DEFAULT_HEIGHT 480
|
||||
#define RGB_OUT 1
|
||||
#define YUV_OUT 2
|
||||
#define HSV_OUT 4
|
||||
#define CAM_V4L \
|
||||
1 /* deprecated. the incomplete support in pygame was removed */
|
||||
#define CAM_V4L2 2
|
||||
|
||||
struct buffer {
|
||||
void *start;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
#if defined(__unix__)
|
||||
typedef struct pgCameraObject {
|
||||
PyObject_HEAD char *device_name;
|
||||
int camera_type;
|
||||
unsigned long pixelformat;
|
||||
unsigned int color_out;
|
||||
struct buffer *buffers;
|
||||
unsigned int n_buffers;
|
||||
int width;
|
||||
int height;
|
||||
int size;
|
||||
int hflip;
|
||||
int vflip;
|
||||
int brightness;
|
||||
int fd;
|
||||
} pgCameraObject;
|
||||
#else
|
||||
/* generic definition.
|
||||
*/
|
||||
|
||||
typedef struct pgCameraObject {
|
||||
PyObject_HEAD char *device_name;
|
||||
int camera_type;
|
||||
unsigned long pixelformat;
|
||||
unsigned int color_out;
|
||||
struct buffer *buffers;
|
||||
unsigned int n_buffers;
|
||||
int width;
|
||||
int height;
|
||||
int size;
|
||||
int hflip;
|
||||
int vflip;
|
||||
int brightness;
|
||||
int fd;
|
||||
} pgCameraObject;
|
||||
#endif
|
||||
|
||||
/* internal functions for colorspace conversion */
|
||||
void
|
||||
colorspace(SDL_Surface *src, SDL_Surface *dst, int cspace);
|
||||
void
|
||||
rgb24_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format);
|
||||
void
|
||||
bgr32_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format);
|
||||
void
|
||||
rgb444_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format);
|
||||
void
|
||||
rgb_to_yuv(const void *src, void *dst, int length, unsigned long source,
|
||||
SDL_PixelFormat *format);
|
||||
void
|
||||
rgb_to_hsv(const void *src, void *dst, int length, unsigned long source,
|
||||
SDL_PixelFormat *format);
|
||||
void
|
||||
yuyv_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format);
|
||||
void
|
||||
yuyv_to_yuv(const void *src, void *dst, int length, SDL_PixelFormat *format);
|
||||
void
|
||||
uyvy_to_rgb(const void *src, void *dst, int length, SDL_PixelFormat *format);
|
||||
void
|
||||
uyvy_to_yuv(const void *src, void *dst, int length, SDL_PixelFormat *format);
|
||||
void
|
||||
sbggr8_to_rgb(const void *src, void *dst, int width, int height,
|
||||
SDL_PixelFormat *format);
|
||||
void
|
||||
yuv420_to_rgb(const void *src, void *dst, int width, int height,
|
||||
SDL_PixelFormat *format);
|
||||
void
|
||||
yuv420_to_yuv(const void *src, void *dst, int width, int height,
|
||||
SDL_PixelFormat *format);
|
||||
|
||||
#if defined(__unix__)
|
||||
/* internal functions specific to v4l2 */
|
||||
char **
|
||||
v4l2_list_cameras(int *num_devices);
|
||||
int
|
||||
v4l2_get_control(int fd, int id, int *value);
|
||||
int
|
||||
v4l2_set_control(int fd, int id, int value);
|
||||
PyObject *
|
||||
v4l2_read_raw(pgCameraObject *self);
|
||||
int
|
||||
v4l2_xioctl(int fd, int request, void *arg);
|
||||
int
|
||||
v4l2_process_image(pgCameraObject *self, const void *image, int buffer_size,
|
||||
SDL_Surface *surf);
|
||||
int
|
||||
v4l2_query_buffer(pgCameraObject *self);
|
||||
int
|
||||
v4l2_read_frame(pgCameraObject *self, SDL_Surface *surf, int *errno_code);
|
||||
int
|
||||
v4l2_stop_capturing(pgCameraObject *self);
|
||||
int
|
||||
v4l2_start_capturing(pgCameraObject *self);
|
||||
int
|
||||
v4l2_uninit_device(pgCameraObject *self);
|
||||
int
|
||||
v4l2_init_mmap(pgCameraObject *self);
|
||||
int
|
||||
v4l2_init_device(pgCameraObject *self);
|
||||
int
|
||||
v4l2_close_device(pgCameraObject *self);
|
||||
int
|
||||
v4l2_open_device(pgCameraObject *self);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* !CAMERA_H */
|
||||
15
venv/include/site/python3.12/pygame/font.h
Normal file
15
venv/include/site/python3.12/pygame/font.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef PGFONT_INTERNAL_H
|
||||
#define PGFONT_INTERNAL_H
|
||||
|
||||
#include <SDL_ttf.h>
|
||||
|
||||
/* test font initialization */
|
||||
#define FONT_INIT_CHECK() \
|
||||
if (!(*(int *)PyFONT_C_API[2])) \
|
||||
return RAISE(pgExc_SDLError, "font system not initialized")
|
||||
|
||||
#include "include/pygame_font.h"
|
||||
|
||||
#define PYGAMEAPI_FONT_NUMSLOTS 3
|
||||
|
||||
#endif /* ~PGFONT_INTERNAL_H */
|
||||
114
venv/include/site/python3.12/pygame/freetype.h
Normal file
114
venv/include/site/python3.12/pygame/freetype.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2009 Vicent Marti
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
#ifndef _PYGAME_FREETYPE_INTERNAL_H_
|
||||
#define _PYGAME_FREETYPE_INTERNAL_H_
|
||||
|
||||
#include "pgcompat.h"
|
||||
#include "pgplatform.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_CACHE_H
|
||||
#include FT_XFREE86_H
|
||||
#include FT_TRIGONOMETRY_H
|
||||
|
||||
/**********************************************************
|
||||
* Global module constants
|
||||
**********************************************************/
|
||||
|
||||
/* Render styles */
|
||||
#define FT_STYLE_NORMAL 0x00
|
||||
#define FT_STYLE_STRONG 0x01
|
||||
#define FT_STYLE_OBLIQUE 0x02
|
||||
#define FT_STYLE_UNDERLINE 0x04
|
||||
#define FT_STYLE_WIDE 0x08
|
||||
#define FT_STYLE_DEFAULT 0xFF
|
||||
|
||||
/* Bounding box modes */
|
||||
#define FT_BBOX_EXACT FT_GLYPH_BBOX_SUBPIXELS
|
||||
#define FT_BBOX_EXACT_GRIDFIT FT_GLYPH_BBOX_GRIDFIT
|
||||
#define FT_BBOX_PIXEL FT_GLYPH_BBOX_TRUNCATE
|
||||
#define FT_BBOX_PIXEL_GRIDFIT FT_GLYPH_BBOX_PIXELS
|
||||
|
||||
/* Rendering flags */
|
||||
#define FT_RFLAG_NONE (0)
|
||||
#define FT_RFLAG_ANTIALIAS (1 << 0)
|
||||
#define FT_RFLAG_AUTOHINT (1 << 1)
|
||||
#define FT_RFLAG_VERTICAL (1 << 2)
|
||||
#define FT_RFLAG_HINTED (1 << 3)
|
||||
#define FT_RFLAG_KERNING (1 << 4)
|
||||
#define FT_RFLAG_TRANSFORM (1 << 5)
|
||||
#define FT_RFLAG_PAD (1 << 6)
|
||||
#define FT_RFLAG_ORIGIN (1 << 7)
|
||||
#define FT_RFLAG_UCS4 (1 << 8)
|
||||
#define FT_RFLAG_USE_BITMAP_STRIKES (1 << 9)
|
||||
#define FT_RFLAG_DEFAULTS \
|
||||
(FT_RFLAG_HINTED | FT_RFLAG_USE_BITMAP_STRIKES | FT_RFLAG_ANTIALIAS)
|
||||
|
||||
#define FT_RENDER_NEWBYTEARRAY 0x0
|
||||
#define FT_RENDER_NEWSURFACE 0x1
|
||||
#define FT_RENDER_EXISTINGSURFACE 0x2
|
||||
|
||||
/**********************************************************
|
||||
* Global module types
|
||||
**********************************************************/
|
||||
|
||||
typedef struct _scale_s {
|
||||
FT_UInt x, y;
|
||||
} Scale_t;
|
||||
typedef FT_Angle Angle_t;
|
||||
|
||||
struct fontinternals_;
|
||||
struct freetypeinstance_;
|
||||
|
||||
typedef struct {
|
||||
FT_Long font_index;
|
||||
FT_Open_Args open_args;
|
||||
} pgFontId;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD pgFontId id;
|
||||
PyObject *path;
|
||||
int is_scalable;
|
||||
int is_bg_col_set;
|
||||
|
||||
Scale_t face_size;
|
||||
FT_Int16 style;
|
||||
FT_Int16 render_flags;
|
||||
double strength;
|
||||
double underline_adjustment;
|
||||
FT_UInt resolution;
|
||||
Angle_t rotation;
|
||||
FT_Matrix transform;
|
||||
FT_Byte fgcolor[4];
|
||||
FT_Byte bgcolor[4];
|
||||
|
||||
struct freetypeinstance_ *freetype; /* Personal reference */
|
||||
struct fontinternals_ *_internals;
|
||||
} pgFontObject;
|
||||
|
||||
#define pgFont_IS_ALIVE(o) (((pgFontObject *)(o))->_internals != 0)
|
||||
|
||||
/* import public API */
|
||||
#include "include/pygame_freetype.h"
|
||||
|
||||
#define PYGAMEAPI_FREETYPE_NUMSLOTS 2
|
||||
|
||||
#endif /* ~_PYGAME_FREETYPE_INTERNAL_H_ */
|
||||
949
venv/include/site/python3.12/pygame/include/_pygame.h
Normal file
949
venv/include/site/python3.12/pygame/include/_pygame.h
Normal file
@@ -0,0 +1,949 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
#ifndef _PYGAME_H
|
||||
#define _PYGAME_H
|
||||
|
||||
/** This header file includes all the definitions for the
|
||||
** base pygame extensions. This header only requires
|
||||
** Python includes (and SDL.h for functions that use SDL types).
|
||||
** The reason for functions prototyped with #define's is
|
||||
** to allow for maximum Python portability. It also uses
|
||||
** Python as the runtime linker, which allows for late binding.
|
||||
'' For more information on this style of development, read
|
||||
** the Python docs on this subject.
|
||||
** http://www.python.org/doc/current/ext/using-cobjects.html
|
||||
**
|
||||
** If using this to build your own derived extensions,
|
||||
** you'll see that the functions available here are mainly
|
||||
** used to help convert between python objects and SDL objects.
|
||||
** Since this library doesn't add a lot of functionality to
|
||||
** the SDL library, it doesn't need to offer a lot either.
|
||||
**
|
||||
** When initializing your extension module, you must manually
|
||||
** import the modules you want to use. (this is the part about
|
||||
** using python as the runtime linker). Each module has its
|
||||
** own import_xxx() routine. You need to perform this import
|
||||
** after you have initialized your own module, and before
|
||||
** you call any routines from that module. Since every module
|
||||
** in pygame does this, there are plenty of examples.
|
||||
**
|
||||
** The base module does include some useful conversion routines
|
||||
** that you are free to use in your own extension.
|
||||
**/
|
||||
|
||||
#include "pgplatform.h"
|
||||
#include <Python.h>
|
||||
|
||||
/* version macros (defined since version 1.9.5) */
|
||||
#define PG_MAJOR_VERSION 2
|
||||
#define PG_MINOR_VERSION 6
|
||||
#define PG_PATCH_VERSION 1
|
||||
#define PG_VERSIONNUM(MAJOR, MINOR, PATCH) \
|
||||
(1000 * (MAJOR) + 100 * (MINOR) + (PATCH))
|
||||
#define PG_VERSION_ATLEAST(MAJOR, MINOR, PATCH) \
|
||||
(PG_VERSIONNUM(PG_MAJOR_VERSION, PG_MINOR_VERSION, PG_PATCH_VERSION) >= \
|
||||
PG_VERSIONNUM(MAJOR, MINOR, PATCH))
|
||||
|
||||
#include "pgcompat.h"
|
||||
|
||||
/* Flag indicating a pg_buffer; used for assertions within callbacks */
|
||||
#ifndef NDEBUG
|
||||
#define PyBUF_PYGAME 0x4000
|
||||
#endif
|
||||
#define PyBUF_HAS_FLAG(f, F) (((f) & (F)) == (F))
|
||||
|
||||
/* Array information exchange struct C type; inherits from Py_buffer
|
||||
*
|
||||
* Pygame uses its own Py_buffer derived C struct as an internal representation
|
||||
* of an imported array buffer. The extended Py_buffer allows for a
|
||||
* per-instance release callback,
|
||||
*/
|
||||
typedef void (*pybuffer_releaseproc)(Py_buffer *);
|
||||
|
||||
typedef struct pg_bufferinfo_s {
|
||||
Py_buffer view;
|
||||
PyObject *consumer; /* Input: Borrowed reference */
|
||||
pybuffer_releaseproc release_buffer;
|
||||
} pg_buffer;
|
||||
|
||||
#include "pgimport.h"
|
||||
|
||||
/*
|
||||
* BASE module
|
||||
*/
|
||||
#ifndef PYGAMEAPI_BASE_INTERNAL
|
||||
#define pgExc_SDLError ((PyObject *)PYGAMEAPI_GET_SLOT(base, 0))
|
||||
|
||||
#define pg_RegisterQuit \
|
||||
(*(void (*)(void (*)(void)))PYGAMEAPI_GET_SLOT(base, 1))
|
||||
|
||||
/**
|
||||
* \brief Convert number like object *obj* to C int and in *val*.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param val A pointer to the C integer to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
* \note This function will convert floats to integers.
|
||||
*/
|
||||
#define pg_IntFromObj \
|
||||
(*(int (*)(PyObject *, int *))PYGAMEAPI_GET_SLOT(base, 2))
|
||||
|
||||
/**
|
||||
* \brief Convert number like object at position *i* in sequence *obj*
|
||||
* to C int and place in argument *val*.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param i The index of the object to convert.
|
||||
* \param val A pointer to the C integer to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
* \note This function will convert floats to integers.
|
||||
*/
|
||||
#define pg_IntFromObjIndex \
|
||||
(*(int (*)(PyObject *, int, int *))PYGAMEAPI_GET_SLOT(base, 3))
|
||||
|
||||
/**
|
||||
* \brief Convert the two number like objects in length 2 sequence *obj* to C
|
||||
* int and place in arguments *val1* and *val2*.
|
||||
*
|
||||
* \param obj The Python two element sequence object to convert.
|
||||
* \param val A pointer to the C integer to store the result.
|
||||
* \param val2 A pointer to the C integer to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
* \note This function will convert floats to integers.
|
||||
*/
|
||||
#define pg_TwoIntsFromObj \
|
||||
(*(int (*)(PyObject *, int *, int *))PYGAMEAPI_GET_SLOT(base, 4))
|
||||
|
||||
/**
|
||||
* \brief Convert number like object *obj* to C float and in *val*.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param val A pointer to the C float to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
*/
|
||||
#define pg_FloatFromObj \
|
||||
(*(int (*)(PyObject *, float *))PYGAMEAPI_GET_SLOT(base, 5))
|
||||
|
||||
/**
|
||||
* \brief Convert number like object at position *i* in sequence *obj* to C
|
||||
* float and place in argument *val*.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param i The index of the object to convert.
|
||||
* \param val A pointer to the C float to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
*/
|
||||
#define pg_FloatFromObjIndex \
|
||||
(*(int (*)(PyObject *, int, float *))PYGAMEAPI_GET_SLOT(base, 6))
|
||||
|
||||
/**
|
||||
* \brief Convert the two number like objects in length 2 sequence *obj* to C
|
||||
* float and place in arguments *val1* and *val2*.
|
||||
*
|
||||
* \param obj The Python two element sequence object to convert.
|
||||
* \param val A pointer to the C float to store the result.
|
||||
* \param val2 A pointer to the C float to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
*/
|
||||
#define pg_TwoFloatsFromObj \
|
||||
(*(int (*)(PyObject *, float *, float *))PYGAMEAPI_GET_SLOT(base, 7))
|
||||
|
||||
/**
|
||||
* \brief Convert number like object *obj* to C Uint32 and in *val*.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param val A pointer to the C int to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*/
|
||||
#define pg_UintFromObj \
|
||||
(*(int (*)(PyObject *, Uint32 *))PYGAMEAPI_GET_SLOT(base, 8))
|
||||
|
||||
/**
|
||||
* \brief Convert number like object at position *i* in sequence *obj* to C
|
||||
* Uint32 and place in argument *val*.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param i The index of the object to convert.
|
||||
* \param val A pointer to the C int to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*/
|
||||
#define pg_UintFromObjIndex \
|
||||
(*(int (*)(PyObject *, int, Uint32 *))PYGAMEAPI_GET_SLOT(base, 9))
|
||||
|
||||
/**
|
||||
* \brief Initialize all of the pygame modules.
|
||||
* \returns 1 on success, 0 on failure with PyErr set.
|
||||
*/
|
||||
#define pg_mod_autoinit (*(int (*)(const char *))PYGAMEAPI_GET_SLOT(base, 10))
|
||||
|
||||
/**
|
||||
* \brief Quit all of the pygame modules.
|
||||
*/
|
||||
#define pg_mod_autoquit (*(void (*)(const char *))PYGAMEAPI_GET_SLOT(base, 11))
|
||||
|
||||
/**
|
||||
* \brief Convert the color represented by object *obj* into a red, green,
|
||||
* blue, alpha length 4 C array *RGBA*.
|
||||
*
|
||||
* The object must be a length 3 or 4 sequence of numbers having values between
|
||||
* 0 and 255 inclusive. For a length 3 sequence an alpha value of 255 is
|
||||
* assumed.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param RGBA A pointer to the C array to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*/
|
||||
#define pg_RGBAFromObj \
|
||||
(*(int (*)(PyObject *, Uint8 *))PYGAMEAPI_GET_SLOT(base, 12))
|
||||
|
||||
/**
|
||||
* \brief Given a Py_buffer, return a python dictionary representing the array
|
||||
* interface.
|
||||
*
|
||||
* \param view_p A pointer to the Py_buffer to convert to a dictionary.
|
||||
*
|
||||
* \returns A Python dictionary representing the array interface of the object.
|
||||
*/
|
||||
#define pgBuffer_AsArrayInterface \
|
||||
(*(PyObject * (*)(Py_buffer *)) PYGAMEAPI_GET_SLOT(base, 13))
|
||||
|
||||
/**
|
||||
* \brief Given a Py_buffer, return a python capsule representing the array
|
||||
* interface.
|
||||
*
|
||||
* \param view_p A pointer to the Py_buffer to convert to a capsule.
|
||||
*
|
||||
* \returns A Python capsule representing the array interface of the object.
|
||||
*/
|
||||
#define pgBuffer_AsArrayStruct \
|
||||
(*(PyObject * (*)(Py_buffer *)) PYGAMEAPI_GET_SLOT(base, 14))
|
||||
|
||||
/**
|
||||
* \brief Get a buffer object from a given Python object.
|
||||
*
|
||||
* \param obj The Python object to get the buffer from.
|
||||
* \param pg_view_p A pointer to a pg_buffer struct to store the buffer in.
|
||||
* \param flags The desired buffer access mode.
|
||||
*
|
||||
* \returns 0 on success, -1 on failure.
|
||||
*
|
||||
* \note This function attempts to get a buffer object from a given Python
|
||||
* object. If the object supports the buffer protocol, it will be used to
|
||||
* create the buffer. If not, it will try to get an array interface or
|
||||
* dictionary representation of the object and use that to create the buffer.
|
||||
* If none of these methods work, it will raise a ValueError.
|
||||
*
|
||||
*/
|
||||
#define pgObject_GetBuffer \
|
||||
(*(int (*)(PyObject *, pg_buffer *, int))PYGAMEAPI_GET_SLOT(base, 15))
|
||||
|
||||
/**
|
||||
* \brief Release a pg_buffer object.
|
||||
*
|
||||
* \param pg_view_p The pg_buffer object to release.
|
||||
*
|
||||
* \note This function releases a pg_buffer object.
|
||||
* \note some calls to this function expect this function to not clear
|
||||
* previously set errors.
|
||||
*/
|
||||
#define pgBuffer_Release (*(void (*)(pg_buffer *))PYGAMEAPI_GET_SLOT(base, 16))
|
||||
|
||||
/**
|
||||
* \brief Write the array interface dictionary buffer description *dict* into a
|
||||
* Pygame buffer description struct *pg_view_p*.
|
||||
*
|
||||
* \param pg_view_p The Pygame buffer description struct to write into.
|
||||
* \param dict The array interface dictionary to read from.
|
||||
* \param flags The PyBUF flags describing the view type requested.
|
||||
*
|
||||
* \returns 0 on success, or -1 on failure.
|
||||
*/
|
||||
#define pgDict_AsBuffer \
|
||||
(*(int (*)(pg_buffer *, PyObject *, int))PYGAMEAPI_GET_SLOT(base, 17))
|
||||
|
||||
#define pgExc_BufferError ((PyObject *)PYGAMEAPI_GET_SLOT(base, 18))
|
||||
|
||||
/**
|
||||
* \brief Get the default SDL window created by a pygame.display.set_mode()
|
||||
* call, or *NULL*.
|
||||
*
|
||||
* \return The default window, or *NULL* if no window has been created.
|
||||
*/
|
||||
#define pg_GetDefaultWindow \
|
||||
(*(SDL_Window * (*)(void)) PYGAMEAPI_GET_SLOT(base, 19))
|
||||
|
||||
/**
|
||||
* \brief Set the default SDL window created by a pygame.display.set_mode()
|
||||
* call. The previous window, if any, is destroyed. Argument *win* may be
|
||||
* *NULL*. This function is called by pygame.display.set_mode().
|
||||
*
|
||||
* \param win The new default window. May be NULL.
|
||||
*/
|
||||
#define pg_SetDefaultWindow \
|
||||
(*(void (*)(SDL_Window *))PYGAMEAPI_GET_SLOT(base, 20))
|
||||
|
||||
/**
|
||||
* \brief Return a borrowed reference to the Pygame default window display
|
||||
* surface, or *NULL* if no default window is open.
|
||||
*
|
||||
* \return The default renderer, or *NULL* if no renderer has been created.
|
||||
*/
|
||||
#define pg_GetDefaultWindowSurface \
|
||||
(*(pgSurfaceObject * (*)(void)) PYGAMEAPI_GET_SLOT(base, 21))
|
||||
|
||||
/**
|
||||
* \brief Set the Pygame default window display surface. The previous
|
||||
* surface, if any, is destroyed. Argument *screen* may be *NULL*. This
|
||||
* function is called by pygame.display.set_mode().
|
||||
*
|
||||
* \param screen The new default window display surface. May be NULL.
|
||||
*/
|
||||
#define pg_SetDefaultWindowSurface \
|
||||
(*(void (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(base, 22))
|
||||
|
||||
/**
|
||||
* \returns NULL if the environment variable PYGAME_BLEND_ALPHA_SDL2 is not
|
||||
* set, otherwise returns a pointer to the environment variable.
|
||||
*/
|
||||
#define pg_EnvShouldBlendAlphaSDL2 \
|
||||
(*(char *(*)(void))PYGAMEAPI_GET_SLOT(base, 23))
|
||||
|
||||
/**
|
||||
* \brief Convert number like object *obj* to C double and in *val*.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param val A pointer to the C double to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
*/
|
||||
#define pg_DoubleFromObj \
|
||||
(*(int (*)(PyObject *, double *))PYGAMEAPI_GET_SLOT(base, 24))
|
||||
|
||||
/**
|
||||
* \brief Convert number like object at position *i* in sequence *obj* to C
|
||||
* double and place in argument *val*.
|
||||
*
|
||||
* \param obj The Python object to convert.
|
||||
* \param i The index of the object to convert.
|
||||
* \param val A pointer to the C double to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
*/
|
||||
#define pg_DoubleFromObjIndex \
|
||||
(*(int (*)(PyObject *, int, double *))PYGAMEAPI_GET_SLOT(base, 25))
|
||||
|
||||
/**
|
||||
* \brief Convert the two number like objects in length 2 sequence *obj* to C
|
||||
* double and place in arguments *val1* and *val2*.
|
||||
*
|
||||
* \param obj The Python two element sequence object to convert.
|
||||
* \param val A pointer to the C double to store the result.
|
||||
* \param val2 A pointer to the C double to store the result.
|
||||
* \returns 1 if the conversion was successful, 0 otherwise.
|
||||
*/
|
||||
#define pg_TwoDoublesFromObj \
|
||||
(*(int (*)(PyObject *, double *, double *))PYGAMEAPI_GET_SLOT(base, 26))
|
||||
|
||||
#define import_pygame_base() IMPORT_PYGAME_MODULE(base)
|
||||
#endif /* ~PYGAMEAPI_BASE_INTERNAL */
|
||||
|
||||
typedef struct {
|
||||
/**
|
||||
* \brief The SDL rect wrapped by this object.
|
||||
*/
|
||||
PyObject_HEAD SDL_Rect r;
|
||||
/**
|
||||
* \brief A list of weak references to this rect.
|
||||
*/
|
||||
PyObject *weakreflist;
|
||||
} pgRectObject;
|
||||
|
||||
/**
|
||||
* \brief Convert a pgRectObject to an SDL_Rect.
|
||||
*
|
||||
* \param obj A pgRectObject instance.
|
||||
* \returns the SDL_Rect field of *obj*, a pgRect_Type instance.
|
||||
*
|
||||
* \note SDL_Rect pgRect_AsRect(PyObject *obj)
|
||||
*/
|
||||
#define pgRect_AsRect(x) (((pgRectObject *)x)->r)
|
||||
|
||||
#ifndef PYGAMEAPI_RECT_INTERNAL
|
||||
|
||||
/**
|
||||
* \brief The Pygame rectangle object type pygame.Rect.
|
||||
*/
|
||||
#define pgRect_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(rect, 0))
|
||||
|
||||
/**
|
||||
* \brief Check if *obj* is a `pygame.Rect` instance.
|
||||
*
|
||||
* \returns true if *obj* is a `pygame.Rect` instance
|
||||
*/
|
||||
#define pgRect_Check(obj) ((obj)->ob_type == &pgRect_Type)
|
||||
|
||||
/**
|
||||
* \brief Create a new `pygame.Rect` instance.
|
||||
*
|
||||
* \param r A pointer to an SDL_Rect struct.
|
||||
* \returns a new `pygame.Rect` object for the SDL_Rect *r*.
|
||||
* Returns *NULL* on error.
|
||||
*
|
||||
* \note PyObject* pgRect_New(SDL_Rect *r)
|
||||
*/
|
||||
#define pgRect_New (*(PyObject * (*)(SDL_Rect *)) PYGAMEAPI_GET_SLOT(rect, 1))
|
||||
|
||||
/**
|
||||
* \brief Create a new `pygame.Rect` instance from x, y, w, h.
|
||||
*
|
||||
* \param x The x coordinate of the rectangle.
|
||||
* \param y The y coordinate of the rectangle.
|
||||
* \param w The width of the rectangle.
|
||||
* \param h The height of the rectangle.
|
||||
* \returns a new `pygame.Rect` object. Returns *NULL* on error.
|
||||
*
|
||||
* \note PyObject* pgRect_New4(int x, int y, int w, int h)
|
||||
*/
|
||||
#define pgRect_New4 \
|
||||
(*(PyObject * (*)(int, int, int, int)) PYGAMEAPI_GET_SLOT(rect, 2))
|
||||
|
||||
/**
|
||||
* \brief Convert a Python object to a `pygame.Rect` instance.
|
||||
*
|
||||
* \param obj A Python object.
|
||||
* A rectangle can be a length 4 sequence integers (x, y, w, h), or a length 2
|
||||
* sequence of position (x, y) and size (w, h), or a length 1 tuple containing
|
||||
* a rectangle representation, or have a method *rect* that returns a
|
||||
* rectangle.
|
||||
*
|
||||
* \param temp A pointer to an SDL_Rect struct to store the result in.
|
||||
* \returns a pointer to the SDL_Rect field of the `pygame.Rect` instance
|
||||
* *obj*. Returns *NULL* on error.
|
||||
*
|
||||
* \note This function will clear any Python errors.
|
||||
* \note SDL_Rect* pgRect_FromObject(PyObject *obj, SDL_Rect *temp)
|
||||
*/
|
||||
#define pgRect_FromObject \
|
||||
(*(SDL_Rect * (*)(PyObject *, SDL_Rect *)) PYGAMEAPI_GET_SLOT(rect, 3))
|
||||
|
||||
/**
|
||||
* \brief Normalize a `pygame.Rect` instance. A rect with a negative size
|
||||
* (negative width and/or height) will be adjusted to have a positive size.
|
||||
*
|
||||
* \param rect A pointer to a `pygame.Rect` instance.
|
||||
* \returns *rect* normalized with positive values only.
|
||||
*
|
||||
* \note void pgRect_Normalize(SDL_Rect *rect)
|
||||
*/
|
||||
#define pgRect_Normalize (*(void (*)(SDL_Rect *))PYGAMEAPI_GET_SLOT(rect, 4))
|
||||
|
||||
#define import_pygame_rect() IMPORT_PYGAME_MODULE(rect)
|
||||
#endif /* ~PYGAMEAPI_RECT_INTERNAL */
|
||||
|
||||
/*
|
||||
* JOYSTICK module
|
||||
*/
|
||||
typedef struct pgJoystickObject {
|
||||
PyObject_HEAD int id;
|
||||
SDL_Joystick *joy;
|
||||
|
||||
/* Joysticks form an intrusive linked list.
|
||||
*
|
||||
* Note that we don't maintain refcounts for these so they are weakrefs
|
||||
* from the Python side.
|
||||
*/
|
||||
struct pgJoystickObject *next;
|
||||
struct pgJoystickObject *prev;
|
||||
} pgJoystickObject;
|
||||
|
||||
#define pgJoystick_AsID(x) (((pgJoystickObject *)x)->id)
|
||||
#define pgJoystick_AsSDL(x) (((pgJoystickObject *)x)->joy)
|
||||
|
||||
#ifndef PYGAMEAPI_JOYSTICK_INTERNAL
|
||||
#define pgJoystick_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(joystick, 0))
|
||||
|
||||
#define pgJoystick_Check(x) ((x)->ob_type == &pgJoystick_Type)
|
||||
#define pgJoystick_New (*(PyObject * (*)(int)) PYGAMEAPI_GET_SLOT(joystick, 1))
|
||||
|
||||
#define import_pygame_joystick() IMPORT_PYGAME_MODULE(joystick)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DISPLAY module
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
Uint32 hw_available : 1;
|
||||
Uint32 wm_available : 1;
|
||||
Uint32 blit_hw : 1;
|
||||
Uint32 blit_hw_CC : 1;
|
||||
Uint32 blit_hw_A : 1;
|
||||
Uint32 blit_sw : 1;
|
||||
Uint32 blit_sw_CC : 1;
|
||||
Uint32 blit_sw_A : 1;
|
||||
Uint32 blit_fill : 1;
|
||||
Uint32 video_mem;
|
||||
SDL_PixelFormat *vfmt;
|
||||
SDL_PixelFormat vfmt_data;
|
||||
int current_w;
|
||||
int current_h;
|
||||
} pg_VideoInfo;
|
||||
|
||||
/**
|
||||
* A pygame object that wraps an SDL_VideoInfo struct.
|
||||
* The object returned by `pygame.display.Info()`
|
||||
*/
|
||||
typedef struct {
|
||||
PyObject_HEAD pg_VideoInfo info;
|
||||
} pgVidInfoObject;
|
||||
|
||||
/**
|
||||
* \brief Convert a pgVidInfoObject to an SDL_VideoInfo.
|
||||
*
|
||||
* \note SDL_VideoInfo pgVidInfo_AsVidInfo(PyObject *obj)
|
||||
*
|
||||
* \returns the SDL_VideoInfo field of *obj*, a pgVidInfo_Type instance.
|
||||
* \param obj A pgVidInfo_Type instance.
|
||||
*
|
||||
* \note Does not check that *obj* is not `NULL` or an `pgVidInfoObject`
|
||||
* object.
|
||||
*/
|
||||
#define pgVidInfo_AsVidInfo(x) (((pgVidInfoObject *)x)->info)
|
||||
|
||||
#ifndef PYGAMEAPI_DISPLAY_INTERNAL
|
||||
/**
|
||||
* \brief The pgVidInfoObject object Python type.
|
||||
* \note pgVideoInfo_Type is used for the `pygame.display.Info()` object.
|
||||
*/
|
||||
#define pgVidInfo_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(display, 0))
|
||||
|
||||
/**
|
||||
* \brief Check if *obj* is a pgVidInfoObject.
|
||||
*
|
||||
* \returns true if *x* is a `pgVidInfo_Type` instance
|
||||
* \note Will return false if *x* is a subclass of `pgVidInfo_Type`.
|
||||
* \note This macro does not check that *x* is not ``NULL``.
|
||||
* \note int pgVidInfo_Check(PyObject *x)
|
||||
*/
|
||||
#define pgVidInfo_Check(x) ((x)->ob_type == &pgVidInfo_Type)
|
||||
|
||||
/**
|
||||
* \brief Create a new pgVidInfoObject.
|
||||
*
|
||||
* \param i A pointer to an SDL_VideoInfo struct.
|
||||
* \returns a new `pgVidInfoObject` object for the SDL_VideoInfo *i*.
|
||||
*
|
||||
* \note PyObject* pgVidInfo_New(SDL_VideoInfo *i)
|
||||
* \note On failure, raise a Python exception and return `NULL`.
|
||||
*/
|
||||
#define pgVidInfo_New \
|
||||
(*(PyObject * (*)(pg_VideoInfo *)) PYGAMEAPI_GET_SLOT(display, 1))
|
||||
|
||||
#define import_pygame_display() IMPORT_PYGAME_MODULE(display)
|
||||
#endif /* ~PYGAMEAPI_DISPLAY_INTERNAL */
|
||||
|
||||
/*
|
||||
* SURFACE module
|
||||
*/
|
||||
struct pgSubSurface_Data;
|
||||
struct SDL_Surface;
|
||||
|
||||
/**
|
||||
* \brief A pygame object that wraps an SDL_Surface. A `pygame.Surface`
|
||||
* instance.
|
||||
*/
|
||||
typedef struct {
|
||||
PyObject_HEAD struct SDL_Surface *surf;
|
||||
/**
|
||||
* \brief If true, the surface will be freed when the python object is
|
||||
* destroyed.
|
||||
*/
|
||||
int owner;
|
||||
/**
|
||||
* \brief The subsurface data for this surface (if a subsurface).
|
||||
*/
|
||||
struct pgSubSurface_Data *subsurface;
|
||||
/**
|
||||
* \brief A list of weak references to this surface.
|
||||
*/
|
||||
PyObject *weakreflist;
|
||||
/**
|
||||
* \brief A list of locks for this surface.
|
||||
*/
|
||||
PyObject *locklist;
|
||||
/**
|
||||
* \brief Usually a buffer object which the surface gets its data from.
|
||||
*/
|
||||
PyObject *dependency;
|
||||
} pgSurfaceObject;
|
||||
|
||||
/**
|
||||
* \brief Convert a `pygame.Surface` instance to an SDL_Surface.
|
||||
*
|
||||
* \param x A `pygame.Surface` instance.
|
||||
* \returns the SDL_Surface field of *x*, a `pygame.Surface` instance.
|
||||
*
|
||||
* \note SDL_Surface* pgSurface_AsSurface(PyObject *x)
|
||||
*/
|
||||
#define pgSurface_AsSurface(x) (((pgSurfaceObject *)x)->surf)
|
||||
|
||||
#ifndef PYGAMEAPI_SURFACE_INTERNAL
|
||||
/**
|
||||
* \brief The `pygame.Surface` object Python type.
|
||||
*/
|
||||
#define pgSurface_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(surface, 0))
|
||||
|
||||
/**
|
||||
* \brief Check if *x* is a `pygame.Surface` instance.
|
||||
*
|
||||
* \param x The object to check.
|
||||
* \returns true if *x* is a `pygame.Surface` instance
|
||||
*
|
||||
* \note Will return false if *x* is a subclass of `pygame.Surface`.
|
||||
* \note This macro does not check that *x* is not ``NULL``.
|
||||
* \note int pgSurface_Check(PyObject *x)
|
||||
*/
|
||||
#define pgSurface_Check(x) \
|
||||
(PyObject_IsInstance((x), (PyObject *)&pgSurface_Type))
|
||||
|
||||
/**
|
||||
* \brief Create a new `pygame.Surface` instance.
|
||||
*
|
||||
* \param s The SDL surface to wrap in a python object.
|
||||
* \param owner If true, the surface will be freed when the python object is
|
||||
* destroyed. \returns A new new pygame surface instance for SDL surface *s*.
|
||||
* Returns *NULL* on error.
|
||||
*
|
||||
* \note pgSurfaceObject* pgSurface_New2(SDL_Surface *s, int owner)
|
||||
*/
|
||||
#define pgSurface_New2 \
|
||||
(*(pgSurfaceObject * (*)(SDL_Surface *, int)) \
|
||||
PYGAMEAPI_GET_SLOT(surface, 1))
|
||||
|
||||
/**
|
||||
* \brief Sets the SDL surface for a `pygame.Surface` instance.
|
||||
*
|
||||
* \param self The `pygame.Surface` instance to set the surface for.
|
||||
* \param s The SDL surface to set.
|
||||
* \param owner If true, the surface will be freed when the python object is
|
||||
* destroyed. \returns 0 on success, -1 on failure.
|
||||
*
|
||||
* \note int pgSurface_SetSurface(pgSurfaceObject *self, SDL_Surface *s, int
|
||||
* owner)
|
||||
*/
|
||||
#define pgSurface_SetSurface \
|
||||
(*(int (*)(pgSurfaceObject *, SDL_Surface *, int))PYGAMEAPI_GET_SLOT( \
|
||||
surface, 3))
|
||||
|
||||
/**
|
||||
* \brief Blit one surface onto another.
|
||||
*
|
||||
* \param dstobj The destination surface.
|
||||
* \param srcobj The source surface.
|
||||
* \param dstrect The destination rectangle.
|
||||
* \param srcrect The source rectangle.
|
||||
* \param the_args The blit flags.
|
||||
* \return 0 for success, -1 or -2 for error.
|
||||
*
|
||||
* \note Is accessible through the C api.
|
||||
* \note int pgSurface_Blit(PyObject *dstobj, PyObject *srcobj, SDL_Rect
|
||||
* *dstrect, SDL_Rect *srcrect, int the_args)
|
||||
*/
|
||||
#define pgSurface_Blit \
|
||||
(*(int (*)(pgSurfaceObject *, pgSurfaceObject *, SDL_Rect *, SDL_Rect *, \
|
||||
int))PYGAMEAPI_GET_SLOT(surface, 2))
|
||||
|
||||
#define import_pygame_surface() \
|
||||
do { \
|
||||
IMPORT_PYGAME_MODULE(surface); \
|
||||
if (PyErr_Occurred() != NULL) \
|
||||
break; \
|
||||
IMPORT_PYGAME_MODULE(surflock); \
|
||||
} while (0)
|
||||
|
||||
#define pgSurface_New(surface) pgSurface_New2((surface), 1)
|
||||
#define pgSurface_NewNoOwn(surface) pgSurface_New2((surface), 0)
|
||||
|
||||
#endif /* ~PYGAMEAPI_SURFACE_INTERNAL */
|
||||
|
||||
/*
|
||||
* SURFLOCK module
|
||||
* auto imported/initialized by surface
|
||||
*/
|
||||
#ifndef PYGAMEAPI_SURFLOCK_INTERNAL
|
||||
#define pgLifetimeLock_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(surflock, 0))
|
||||
|
||||
#define pgLifetimeLock_Check(x) ((x)->ob_type == &pgLifetimeLock_Type)
|
||||
|
||||
#define pgSurface_Prep(x) \
|
||||
if ((x)->subsurface) \
|
||||
(*(*(void (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(surflock, 1)))(x)
|
||||
|
||||
#define pgSurface_Unprep(x) \
|
||||
if ((x)->subsurface) \
|
||||
(*(*(void (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(surflock, 2)))(x)
|
||||
|
||||
#define pgSurface_Lock \
|
||||
(*(int (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(surflock, 3))
|
||||
|
||||
#define pgSurface_Unlock \
|
||||
(*(int (*)(pgSurfaceObject *))PYGAMEAPI_GET_SLOT(surflock, 4))
|
||||
|
||||
#define pgSurface_LockBy \
|
||||
(*(int (*)(pgSurfaceObject *, PyObject *))PYGAMEAPI_GET_SLOT(surflock, 5))
|
||||
|
||||
#define pgSurface_UnlockBy \
|
||||
(*(int (*)(pgSurfaceObject *, PyObject *))PYGAMEAPI_GET_SLOT(surflock, 6))
|
||||
|
||||
#define pgSurface_LockLifetime \
|
||||
(*(PyObject * (*)(PyObject *, PyObject *)) PYGAMEAPI_GET_SLOT(surflock, 7))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* EVENT module
|
||||
*/
|
||||
typedef struct pgEventObject pgEventObject;
|
||||
|
||||
#ifndef PYGAMEAPI_EVENT_INTERNAL
|
||||
#define pgEvent_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(event, 0))
|
||||
|
||||
#define pgEvent_Check(x) ((x)->ob_type == &pgEvent_Type)
|
||||
|
||||
#define pgEvent_New \
|
||||
(*(PyObject * (*)(SDL_Event *)) PYGAMEAPI_GET_SLOT(event, 1))
|
||||
|
||||
#define pgEvent_New2 \
|
||||
(*(PyObject * (*)(int, PyObject *)) PYGAMEAPI_GET_SLOT(event, 2))
|
||||
|
||||
#define pgEvent_FillUserEvent \
|
||||
(*(int (*)(pgEventObject *, SDL_Event *))PYGAMEAPI_GET_SLOT(event, 3))
|
||||
|
||||
#define pg_EnableKeyRepeat (*(int (*)(int, int))PYGAMEAPI_GET_SLOT(event, 4))
|
||||
|
||||
#define pg_GetKeyRepeat (*(void (*)(int *, int *))PYGAMEAPI_GET_SLOT(event, 5))
|
||||
|
||||
#define import_pygame_event() IMPORT_PYGAME_MODULE(event)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* RWOBJECT module
|
||||
* the rwobject are only needed for C side work, not accessible from python.
|
||||
*/
|
||||
#ifndef PYGAMEAPI_RWOBJECT_INTERNAL
|
||||
#define pgRWops_FromObject \
|
||||
(*(SDL_RWops * (*)(PyObject *, char **)) PYGAMEAPI_GET_SLOT(rwobject, 0))
|
||||
|
||||
#define pgRWops_IsFileObject \
|
||||
(*(int (*)(SDL_RWops *))PYGAMEAPI_GET_SLOT(rwobject, 1))
|
||||
|
||||
#define pg_EncodeFilePath \
|
||||
(*(PyObject * (*)(PyObject *, PyObject *)) PYGAMEAPI_GET_SLOT(rwobject, 2))
|
||||
|
||||
#define pg_EncodeString \
|
||||
(*(PyObject * (*)(PyObject *, const char *, const char *, PyObject *)) \
|
||||
PYGAMEAPI_GET_SLOT(rwobject, 3))
|
||||
|
||||
#define pgRWops_FromFileObject \
|
||||
(*(SDL_RWops * (*)(PyObject *)) PYGAMEAPI_GET_SLOT(rwobject, 4))
|
||||
|
||||
#define pgRWops_ReleaseObject \
|
||||
(*(int (*)(SDL_RWops *))PYGAMEAPI_GET_SLOT(rwobject, 5))
|
||||
|
||||
#define import_pygame_rwobject() IMPORT_PYGAME_MODULE(rwobject)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* PixelArray module
|
||||
*/
|
||||
#ifndef PYGAMEAPI_PIXELARRAY_INTERNAL
|
||||
#define PyPixelArray_Type ((PyTypeObject *)PYGAMEAPI_GET_SLOT(pixelarray, 0))
|
||||
|
||||
#define PyPixelArray_Check(x) ((x)->ob_type == &PyPixelArray_Type)
|
||||
#define PyPixelArray_New (*(PyObject * (*)) PYGAMEAPI_GET_SLOT(pixelarray, 1))
|
||||
|
||||
#define import_pygame_pixelarray() IMPORT_PYGAME_MODULE(pixelarray)
|
||||
#endif /* PYGAMEAPI_PIXELARRAY_INTERNAL */
|
||||
|
||||
/*
|
||||
* Color module
|
||||
*/
|
||||
typedef struct pgColorObject pgColorObject;
|
||||
|
||||
#ifndef PYGAMEAPI_COLOR_INTERNAL
|
||||
#define pgColor_Type (*(PyObject *)PYGAMEAPI_GET_SLOT(color, 0))
|
||||
|
||||
#define pgColor_Check(x) ((x)->ob_type == &pgColor_Type)
|
||||
#define pgColor_New (*(PyObject * (*)(Uint8 *)) PYGAMEAPI_GET_SLOT(color, 1))
|
||||
|
||||
#define pgColor_NewLength \
|
||||
(*(PyObject * (*)(Uint8 *, Uint8)) PYGAMEAPI_GET_SLOT(color, 3))
|
||||
|
||||
#define pg_RGBAFromColorObj \
|
||||
(*(int (*)(PyObject *, Uint8 *))PYGAMEAPI_GET_SLOT(color, 2))
|
||||
|
||||
#define pg_RGBAFromFuzzyColorObj \
|
||||
(*(int (*)(PyObject *, Uint8 *))PYGAMEAPI_GET_SLOT(color, 4))
|
||||
|
||||
#define pgColor_AsArray(x) (((pgColorObject *)x)->data)
|
||||
#define pgColor_NumComponents(x) (((pgColorObject *)x)->len)
|
||||
|
||||
#define import_pygame_color() IMPORT_PYGAME_MODULE(color)
|
||||
#endif /* PYGAMEAPI_COLOR_INTERNAL */
|
||||
|
||||
/*
|
||||
* Math module
|
||||
*/
|
||||
#ifndef PYGAMEAPI_MATH_INTERNAL
|
||||
#define pgVector2_Check(x) \
|
||||
((x)->ob_type == (PyTypeObject *)PYGAMEAPI_GET_SLOT(math, 0))
|
||||
|
||||
#define pgVector3_Check(x) \
|
||||
((x)->ob_type == (PyTypeObject *)PYGAMEAPI_GET_SLOT(math, 1))
|
||||
/*
|
||||
#define pgVector2_New \
|
||||
(*(PyObject*(*)) \
|
||||
PYGAMEAPI_GET_SLOT(PyGAME_C_API, 1))
|
||||
*/
|
||||
#define import_pygame_math() IMPORT_PYGAME_MODULE(math)
|
||||
#endif /* PYGAMEAPI_MATH_INTERNAL */
|
||||
|
||||
#define IMPORT_PYGAME_MODULE _IMPORT_PYGAME_MODULE
|
||||
|
||||
/*
|
||||
* base pygame API slots
|
||||
* disable slots with NO_PYGAME_C_API
|
||||
*/
|
||||
#ifdef PYGAME_H
|
||||
PYGAMEAPI_DEFINE_SLOTS(base);
|
||||
PYGAMEAPI_DEFINE_SLOTS(rect);
|
||||
PYGAMEAPI_DEFINE_SLOTS(cdrom);
|
||||
PYGAMEAPI_DEFINE_SLOTS(joystick);
|
||||
PYGAMEAPI_DEFINE_SLOTS(display);
|
||||
PYGAMEAPI_DEFINE_SLOTS(surface);
|
||||
PYGAMEAPI_DEFINE_SLOTS(surflock);
|
||||
PYGAMEAPI_DEFINE_SLOTS(event);
|
||||
PYGAMEAPI_DEFINE_SLOTS(rwobject);
|
||||
PYGAMEAPI_DEFINE_SLOTS(pixelarray);
|
||||
PYGAMEAPI_DEFINE_SLOTS(color);
|
||||
PYGAMEAPI_DEFINE_SLOTS(math);
|
||||
#else /* ~PYGAME_H */
|
||||
PYGAMEAPI_EXTERN_SLOTS(base);
|
||||
PYGAMEAPI_EXTERN_SLOTS(rect);
|
||||
PYGAMEAPI_EXTERN_SLOTS(cdrom);
|
||||
PYGAMEAPI_EXTERN_SLOTS(joystick);
|
||||
PYGAMEAPI_EXTERN_SLOTS(display);
|
||||
PYGAMEAPI_EXTERN_SLOTS(surface);
|
||||
PYGAMEAPI_EXTERN_SLOTS(surflock);
|
||||
PYGAMEAPI_EXTERN_SLOTS(event);
|
||||
PYGAMEAPI_EXTERN_SLOTS(rwobject);
|
||||
PYGAMEAPI_EXTERN_SLOTS(pixelarray);
|
||||
PYGAMEAPI_EXTERN_SLOTS(color);
|
||||
PYGAMEAPI_EXTERN_SLOTS(math);
|
||||
#endif /* ~PYGAME_H */
|
||||
|
||||
#endif /* PYGAME_H */
|
||||
|
||||
/* Use the end of this file for other cross module inline utility
|
||||
* functions There seems to be no good reason to stick to macro only
|
||||
* functions in Python 3.
|
||||
*/
|
||||
|
||||
static PG_INLINE PyObject *
|
||||
pg_tuple_couple_from_values_int(int val1, int val2)
|
||||
{
|
||||
/* This function turns two input integers into a python tuple object.
|
||||
* Currently, 5th November 2022, this is faster than using Py_BuildValue
|
||||
* to do the same thing.
|
||||
*/
|
||||
PyObject *tup = PyTuple_New(2);
|
||||
if (!tup) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *tmp = PyLong_FromLong(val1);
|
||||
if (!tmp) {
|
||||
Py_DECREF(tup);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(tup, 0, tmp);
|
||||
|
||||
tmp = PyLong_FromLong(val2);
|
||||
if (!tmp) {
|
||||
Py_DECREF(tup);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(tup, 1, tmp);
|
||||
|
||||
return tup;
|
||||
}
|
||||
|
||||
static PG_INLINE PyObject *
|
||||
pg_tuple_triple_from_values_int(int val1, int val2, int val3)
|
||||
{
|
||||
/* This function turns three input integers into a python tuple object.
|
||||
* Currently, 5th November 2022, this is faster than using Py_BuildValue
|
||||
* to do the same thing.
|
||||
*/
|
||||
PyObject *tup = PyTuple_New(3);
|
||||
if (!tup) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *tmp = PyLong_FromLong(val1);
|
||||
if (!tmp) {
|
||||
Py_DECREF(tup);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(tup, 0, tmp);
|
||||
|
||||
tmp = PyLong_FromLong(val2);
|
||||
if (!tmp) {
|
||||
Py_DECREF(tup);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(tup, 1, tmp);
|
||||
|
||||
tmp = PyLong_FromLong(val3);
|
||||
if (!tmp) {
|
||||
Py_DECREF(tup);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(tup, 2, tmp);
|
||||
|
||||
return tup;
|
||||
}
|
||||
171
venv/include/site/python3.12/pygame/include/bitmask.h
Normal file
171
venv/include/site/python3.12/pygame/include/bitmask.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
Bitmask 1.7 - A pixel-perfect collision detection library.
|
||||
|
||||
Copyright (C) 2002-2005 Ulf Ekstrom except for the bitcount
|
||||
function which is copyright (C) Donald W. Gillies, 1992.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef BITMASK_H
|
||||
#define BITMASK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
/* Define INLINE for different compilers. If your compiler does not
|
||||
support inlining then there might be a performance hit in
|
||||
bitmask_overlap_area().
|
||||
*/
|
||||
#ifndef INLINE
|
||||
#ifdef __GNUC__
|
||||
#define INLINE inline
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
#define INLINE __inline
|
||||
#else
|
||||
#define INLINE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define BITMASK_W unsigned long int
|
||||
#define BITMASK_W_LEN (sizeof(BITMASK_W) * CHAR_BIT)
|
||||
#define BITMASK_W_MASK (BITMASK_W_LEN - 1)
|
||||
#define BITMASK_N(n) ((BITMASK_W)1 << (n))
|
||||
|
||||
typedef struct bitmask {
|
||||
int w, h;
|
||||
BITMASK_W bits[1];
|
||||
} bitmask_t;
|
||||
|
||||
/* Creates a bitmask of width w and height h, where
|
||||
w and h must both be greater than or equal to 0.
|
||||
The mask is automatically cleared when created.
|
||||
*/
|
||||
bitmask_t *
|
||||
bitmask_create(int w, int h);
|
||||
|
||||
/* Frees all the memory allocated by bitmask_create for m. */
|
||||
void
|
||||
bitmask_free(bitmask_t *m);
|
||||
|
||||
/* Create a copy of the given bitmask. */
|
||||
bitmask_t *
|
||||
bitmask_copy(bitmask_t *m);
|
||||
|
||||
/* Clears all bits in the mask */
|
||||
void
|
||||
bitmask_clear(bitmask_t *m);
|
||||
|
||||
/* Sets all bits in the mask */
|
||||
void
|
||||
bitmask_fill(bitmask_t *m);
|
||||
|
||||
/* Flips all bits in the mask */
|
||||
void
|
||||
bitmask_invert(bitmask_t *m);
|
||||
|
||||
/* Counts the bits in the mask */
|
||||
unsigned int
|
||||
bitmask_count(bitmask_t *m);
|
||||
|
||||
/* Returns nonzero if the bit at (x,y) is set. Coordinates start at
|
||||
(0,0) */
|
||||
static INLINE int
|
||||
bitmask_getbit(const bitmask_t *m, int x, int y)
|
||||
{
|
||||
return (m->bits[x / BITMASK_W_LEN * m->h + y] &
|
||||
BITMASK_N(x & BITMASK_W_MASK)) != 0;
|
||||
}
|
||||
|
||||
/* Sets the bit at (x,y) */
|
||||
static INLINE void
|
||||
bitmask_setbit(bitmask_t *m, int x, int y)
|
||||
{
|
||||
m->bits[x / BITMASK_W_LEN * m->h + y] |= BITMASK_N(x & BITMASK_W_MASK);
|
||||
}
|
||||
|
||||
/* Clears the bit at (x,y) */
|
||||
static INLINE void
|
||||
bitmask_clearbit(bitmask_t *m, int x, int y)
|
||||
{
|
||||
m->bits[x / BITMASK_W_LEN * m->h + y] &= ~BITMASK_N(x & BITMASK_W_MASK);
|
||||
}
|
||||
|
||||
/* Returns nonzero if the masks overlap with the given offset.
|
||||
The overlap tests uses the following offsets (which may be negative):
|
||||
|
||||
+----+----------..
|
||||
|A | yoffset
|
||||
| +-+----------..
|
||||
+--|B
|
||||
|xoffset
|
||||
| |
|
||||
: :
|
||||
*/
|
||||
int
|
||||
bitmask_overlap(const bitmask_t *a, const bitmask_t *b, int xoffset,
|
||||
int yoffset);
|
||||
|
||||
/* Like bitmask_overlap(), but will also give a point of intersection.
|
||||
x and y are given in the coordinates of mask a, and are untouched
|
||||
if there is no overlap. */
|
||||
int
|
||||
bitmask_overlap_pos(const bitmask_t *a, const bitmask_t *b, int xoffset,
|
||||
int yoffset, int *x, int *y);
|
||||
|
||||
/* Returns the number of overlapping 'pixels' */
|
||||
int
|
||||
bitmask_overlap_area(const bitmask_t *a, const bitmask_t *b, int xoffset,
|
||||
int yoffset);
|
||||
|
||||
/* Fills a mask with the overlap of two other masks. A bitwise AND. */
|
||||
void
|
||||
bitmask_overlap_mask(const bitmask_t *a, const bitmask_t *b, bitmask_t *c,
|
||||
int xoffset, int yoffset);
|
||||
|
||||
/* Draws mask b onto mask a (bitwise OR). Can be used to compose large
|
||||
(game background?) mask from several submasks, which may speed up
|
||||
the testing. */
|
||||
|
||||
void
|
||||
bitmask_draw(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset);
|
||||
|
||||
void
|
||||
bitmask_erase(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset);
|
||||
|
||||
/* Return a new scaled bitmask, with dimensions w*h. The quality of the
|
||||
scaling may not be perfect for all circumstances, but it should
|
||||
be reasonable. If either w or h is 0 a clear 1x1 mask is returned. */
|
||||
bitmask_t *
|
||||
bitmask_scale(const bitmask_t *m, int w, int h);
|
||||
|
||||
/* Convolve b into a, drawing the output into o, shifted by offset. If offset
|
||||
* is 0, then the (x,y) bit will be set if and only if
|
||||
* bitmask_overlap(a, b, x - b->w - 1, y - b->h - 1) returns true.
|
||||
*
|
||||
* Modifies bits o[xoffset ... xoffset + a->w + b->w - 1)
|
||||
* [yoffset ... yoffset + a->h + b->h - 1). */
|
||||
void
|
||||
bitmask_convolve(const bitmask_t *a, const bitmask_t *b, bitmask_t *o,
|
||||
int xoffset, int yoffset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of extern "C" { */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
102
venv/include/site/python3.12/pygame/include/pgcompat.h
Normal file
102
venv/include/site/python3.12/pygame/include/pgcompat.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#if !defined(PGCOMPAT_H)
|
||||
#define PGCOMPAT_H
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
/* In CPython, Py_Exit finalises the python interpreter before calling C exit()
|
||||
* This does not exist on PyPy, so use exit() directly here */
|
||||
#ifdef PYPY_VERSION
|
||||
#define PG_EXIT(n) exit(n)
|
||||
#else
|
||||
#define PG_EXIT(n) Py_Exit(n)
|
||||
#endif
|
||||
|
||||
/* define common types where SDL is not included */
|
||||
#ifndef SDL_VERSION_ATLEAST
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
typedef uint32_t Uint32;
|
||||
typedef uint8_t Uint8;
|
||||
#endif /* no SDL */
|
||||
|
||||
#if defined(SDL_VERSION_ATLEAST)
|
||||
|
||||
#ifndef SDL_WINDOW_VULKAN
|
||||
#define SDL_WINDOW_VULKAN 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_ALWAYS_ON_TOP
|
||||
#define SDL_WINDOW_ALWAYS_ON_TOP 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_SKIP_TASKBAR
|
||||
#define SDL_WINDOW_SKIP_TASKBAR 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_UTILITY
|
||||
#define SDL_WINDOW_UTILITY 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_TOOLTIP
|
||||
#define SDL_WINDOW_TOOLTIP 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_POPUP_MENU
|
||||
#define SDL_WINDOW_POPUP_MENU 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_INPUT_GRABBED
|
||||
#define SDL_WINDOW_INPUT_GRABBED 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_INPUT_FOCUS
|
||||
#define SDL_WINDOW_INPUT_FOCUS 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_MOUSE_FOCUS
|
||||
#define SDL_WINDOW_MOUSE_FOCUS 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_FOREIGN
|
||||
#define SDL_WINDOW_FOREIGN 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_ALLOW_HIGHDPI
|
||||
#define SDL_WINDOW_ALLOW_HIGHDPI 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_MOUSE_CAPTURE
|
||||
#define SDL_WINDOW_MOUSE_CAPTURE 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_ALWAYS_ON_TOP
|
||||
#define SDL_WINDOW_ALWAYS_ON_TOP 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_SKIP_TASKBAR
|
||||
#define SDL_WINDOW_SKIP_TASKBAR 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_UTILITY
|
||||
#define SDL_WINDOW_UTILITY 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_TOOLTIP
|
||||
#define SDL_WINDOW_TOOLTIP 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_WINDOW_POPUP_MENU
|
||||
#define SDL_WINDOW_POPUP_MENU 0
|
||||
#endif
|
||||
|
||||
#ifndef SDL_MOUSEWHEEL_FLIPPED
|
||||
#define NO_SDL_MOUSEWHEEL_FLIPPED
|
||||
#endif
|
||||
|
||||
#endif /* defined(SDL_VERSION_ATLEAST) */
|
||||
|
||||
#endif /* ~defined(PGCOMPAT_H) */
|
||||
67
venv/include/site/python3.12/pygame/include/pgimport.h
Normal file
67
venv/include/site/python3.12/pygame/include/pgimport.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef PGIMPORT_H
|
||||
#define PGIMPORT_H
|
||||
|
||||
/* Prefix when importing module */
|
||||
#define IMPPREFIX "pygame."
|
||||
|
||||
#include "pgcompat.h"
|
||||
|
||||
#define PYGAMEAPI_LOCAL_ENTRY "_PYGAME_C_API"
|
||||
#define PG_CAPSULE_NAME(m) (IMPPREFIX m "." PYGAMEAPI_LOCAL_ENTRY)
|
||||
|
||||
/*
|
||||
* fill API slots defined by PYGAMEAPI_DEFINE_SLOTS/PYGAMEAPI_EXTERN_SLOTS
|
||||
*/
|
||||
#define _IMPORT_PYGAME_MODULE(module) \
|
||||
{ \
|
||||
PyObject *_mod_##module = PyImport_ImportModule(IMPPREFIX #module); \
|
||||
\
|
||||
if (_mod_##module != NULL) { \
|
||||
PyObject *_c_api = \
|
||||
PyObject_GetAttrString(_mod_##module, PYGAMEAPI_LOCAL_ENTRY); \
|
||||
\
|
||||
Py_DECREF(_mod_##module); \
|
||||
if (_c_api != NULL && PyCapsule_CheckExact(_c_api)) { \
|
||||
void **localptr = (void **)PyCapsule_GetPointer( \
|
||||
_c_api, PG_CAPSULE_NAME(#module)); \
|
||||
_PGSLOTS_##module = localptr; \
|
||||
} \
|
||||
Py_XDECREF(_c_api); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PYGAMEAPI_IS_IMPORTED(module) (_PGSLOTS_##module != NULL)
|
||||
|
||||
/*
|
||||
* source file must include one of these in order to use _IMPORT_PYGAME_MODULE.
|
||||
* this is set by import_pygame_*() functions.
|
||||
* disable with NO_PYGAME_C_API
|
||||
*/
|
||||
#define PYGAMEAPI_DEFINE_SLOTS(module) void **_PGSLOTS_##module = NULL
|
||||
#define PYGAMEAPI_EXTERN_SLOTS(module) extern void **_PGSLOTS_##module
|
||||
#define PYGAMEAPI_GET_SLOT(module, index) _PGSLOTS_##module[(index)]
|
||||
|
||||
/*
|
||||
* disabled API with NO_PYGAME_C_API; do nothing instead
|
||||
*/
|
||||
#ifdef NO_PYGAME_C_API
|
||||
|
||||
#undef PYGAMEAPI_DEFINE_SLOTS
|
||||
#undef PYGAMEAPI_EXTERN_SLOTS
|
||||
|
||||
#define PYGAMEAPI_DEFINE_SLOTS(module)
|
||||
#define PYGAMEAPI_EXTERN_SLOTS(module)
|
||||
|
||||
/* intentionally leave this defined to cause a compiler error *
|
||||
#define PYGAMEAPI_GET_SLOT(api_root, index)
|
||||
#undef PYGAMEAPI_GET_SLOT*/
|
||||
|
||||
#undef _IMPORT_PYGAME_MODULE
|
||||
#define _IMPORT_PYGAME_MODULE(module)
|
||||
|
||||
#endif /* NO_PYGAME_C_API */
|
||||
|
||||
#define encapsulate_api(ptr, module) \
|
||||
PyCapsule_New(ptr, PG_CAPSULE_NAME(module), NULL)
|
||||
|
||||
#endif /* ~PGIMPORT_H */
|
||||
83
venv/include/site/python3.12/pygame/include/pgplatform.h
Normal file
83
venv/include/site/python3.12/pygame/include/pgplatform.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* platform/compiler adjustments */
|
||||
#ifndef PG_PLATFORM_H
|
||||
#define PG_PLATFORM_H
|
||||
|
||||
#if defined(HAVE_SNPRINTF) /* defined in python.h (pyerrors.h) and SDL.h \
|
||||
(SDL_config.h) */
|
||||
#undef HAVE_SNPRINTF /* remove GCC redefine warning */
|
||||
#endif /* HAVE_SNPRINTF */
|
||||
|
||||
#ifndef PG_INLINE
|
||||
#if defined(__clang__)
|
||||
#define PG_INLINE __inline__ __attribute__((__unused__))
|
||||
#elif defined(__GNUC__)
|
||||
#define PG_INLINE __inline__
|
||||
#elif defined(_MSC_VER)
|
||||
#define PG_INLINE __inline
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
#define PG_INLINE inline
|
||||
#else
|
||||
#define PG_INLINE
|
||||
#endif
|
||||
#endif /* ~PG_INLINE */
|
||||
|
||||
// Worth trying this on MSVC/win32 builds to see if provides any speed up
|
||||
#ifndef PG_FORCEINLINE
|
||||
#if defined(__clang__)
|
||||
#define PG_FORCEINLINE __inline__ __attribute__((__unused__))
|
||||
#elif defined(__GNUC__)
|
||||
#define PG_FORCEINLINE __inline__
|
||||
#elif defined(_MSC_VER)
|
||||
#define PG_FORCEINLINE __forceinline
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
#define PG_FORCEINLINE inline
|
||||
#else
|
||||
#define PG_FORCEINLINE
|
||||
#endif
|
||||
#endif /* ~PG_FORCEINLINE */
|
||||
|
||||
/* This is unconditionally defined in Python.h */
|
||||
#if defined(_POSIX_C_SOURCE)
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SNPRINTF)
|
||||
#undef HAVE_SNPRINTF
|
||||
#endif
|
||||
|
||||
/* SDL needs WIN32 */
|
||||
#if !defined(WIN32) && \
|
||||
(defined(MS_WIN32) || defined(_WIN32) || defined(__WIN32) || \
|
||||
defined(__WIN32__) || defined(_WINDOWS))
|
||||
#define WIN32
|
||||
#endif
|
||||
|
||||
#ifndef PG_TARGET_SSE4_2
|
||||
#if defined(__clang__) || \
|
||||
(defined(__GNUC__) && \
|
||||
((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || __GNUC__ >= 5))
|
||||
// The old gcc 4.8 on centos used by manylinux1 does not seem to get sse4.2
|
||||
// intrinsics
|
||||
#define PG_FUNCTION_TARGET_SSE4_2 __attribute__((target("sse4.2")))
|
||||
// No else; we define the fallback later
|
||||
#endif
|
||||
#endif /* ~PG_TARGET_SSE4_2 */
|
||||
|
||||
#ifdef PG_FUNCTION_TARGET_SSE4_2
|
||||
#if !defined(__SSE4_2__) && !defined(PG_COMPILE_SSE4_2)
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#define PG_COMPILE_SSE4_2 1
|
||||
#endif
|
||||
#endif
|
||||
#endif /* ~PG_TARGET_SSE4_2 */
|
||||
|
||||
/* Fallback definition of target attribute */
|
||||
#ifndef PG_FUNCTION_TARGET_SSE4_2
|
||||
#define PG_FUNCTION_TARGET_SSE4_2
|
||||
#endif
|
||||
|
||||
#ifndef PG_COMPILE_SSE4_2
|
||||
#define PG_COMPILE_SSE4_2 0
|
||||
#endif
|
||||
|
||||
#endif /* ~PG_PLATFORM_H */
|
||||
34
venv/include/site/python3.12/pygame/include/pygame.h
Normal file
34
venv/include/site/python3.12/pygame/include/pygame.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
/* To allow the Pygame C api to be globally shared by all code within an
|
||||
* extension module built from multiple C files, only include the pygame.h
|
||||
* header within the top level C file, the one which calls the
|
||||
* 'import_pygame_*' macros. All other C source files of the module should
|
||||
* include _pygame.h instead.
|
||||
*/
|
||||
#ifndef PYGAME_H
|
||||
#define PYGAME_H
|
||||
|
||||
#include "_pygame.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
Copyright (C) 2007 Rene Dudfield, Richard Goedeken
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
/* Bufferproxy module C api. */
|
||||
#if !defined(PG_BUFPROXY_HEADER)
|
||||
#define PG_BUFPROXY_HEADER
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
typedef PyObject *(*_pgbufproxy_new_t)(PyObject *, getbufferproc);
|
||||
typedef PyObject *(*_pgbufproxy_get_obj_t)(PyObject *);
|
||||
typedef int (*_pgbufproxy_trip_t)(PyObject *);
|
||||
|
||||
#ifndef PYGAMEAPI_BUFPROXY_INTERNAL
|
||||
|
||||
#include "pgimport.h"
|
||||
|
||||
PYGAMEAPI_DEFINE_SLOTS(bufferproxy);
|
||||
|
||||
#define pgBufproxy_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(bufferproxy, 0))
|
||||
|
||||
#define pgBufproxy_Check(x) ((x)->ob_type == &pgBufproxy_Type)
|
||||
|
||||
#define pgBufproxy_New (*(_pgbufproxy_new_t)PYGAMEAPI_GET_SLOT(bufferproxy, 1))
|
||||
|
||||
#define pgBufproxy_GetParent \
|
||||
(*(_pgbufproxy_get_obj_t)PYGAMEAPI_GET_SLOT(bufferproxy, 2))
|
||||
|
||||
#define pgBufproxy_Trip \
|
||||
(*(_pgbufproxy_trip_t)PYGAMEAPI_GET_SLOT(bufferproxy, 3))
|
||||
|
||||
#define import_pygame_bufferproxy() _IMPORT_PYGAME_MODULE(bufferproxy)
|
||||
|
||||
#endif /* ~PYGAMEAPI_BUFPROXY_INTERNAL */
|
||||
|
||||
#endif /* ~defined(PG_BUFPROXY_HEADER) */
|
||||
50
venv/include/site/python3.12/pygame/include/pygame_font.h
Normal file
50
venv/include/site/python3.12/pygame/include/pygame_font.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
#include "pgplatform.h"
|
||||
|
||||
struct TTF_Font;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD TTF_Font *font;
|
||||
PyObject *weakreflist;
|
||||
unsigned int ttf_init_generation;
|
||||
} PyFontObject;
|
||||
#define PyFont_AsFont(x) (((PyFontObject *)x)->font)
|
||||
|
||||
#ifndef PYGAMEAPI_FONT_INTERNAL
|
||||
|
||||
#include "pgimport.h"
|
||||
|
||||
PYGAMEAPI_DEFINE_SLOTS(font);
|
||||
|
||||
#define PyFont_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(font, 0))
|
||||
#define PyFont_Check(x) ((x)->ob_type == &PyFont_Type)
|
||||
|
||||
#define PyFont_New (*(PyObject * (*)(TTF_Font *)) PYGAMEAPI_GET_SLOT(font, 1))
|
||||
|
||||
/*slot 2 taken by FONT_INIT_CHECK*/
|
||||
|
||||
#define import_pygame_font() _IMPORT_PYGAME_MODULE(font)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2009 Vicent Marti
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
#ifndef PYGAME_FREETYPE_H_
|
||||
#define PYGAME_FREETYPE_H_
|
||||
|
||||
#include "pgplatform.h"
|
||||
#include "pgimport.h"
|
||||
#include "pgcompat.h"
|
||||
|
||||
#ifndef PYGAME_FREETYPE_INTERNAL
|
||||
|
||||
PYGAMEAPI_DEFINE_SLOTS(_freetype);
|
||||
|
||||
#define pgFont_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(_freetype, 0))
|
||||
|
||||
#define pgFont_Check(x) ((x)->ob_type == &pgFont_Type)
|
||||
|
||||
#define pgFont_New \
|
||||
(*(PyObject * (*)(const char *, long)) PYGAMEAPI_GET_SLOT(_freetype, 1))
|
||||
|
||||
#define import_pygame_freetype() _IMPORT_PYGAME_MODULE(_freetype)
|
||||
|
||||
#endif /* PYGAME_FREETYPE_INTERNAL */
|
||||
|
||||
#endif /* PYGAME_FREETYPE_H_ */
|
||||
45
venv/include/site/python3.12/pygame/include/pygame_mask.h
Normal file
45
venv/include/site/python3.12/pygame/include/pygame_mask.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef PGMASK_H
|
||||
#define PGMASK_H
|
||||
|
||||
#include <Python.h>
|
||||
#include "bitmask.h"
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD bitmask_t *mask;
|
||||
void *bufdata;
|
||||
} pgMaskObject;
|
||||
|
||||
#define pgMask_AsBitmap(x) (((pgMaskObject *)x)->mask)
|
||||
|
||||
#ifndef PYGAMEAPI_MASK_INTERNAL
|
||||
|
||||
#include "pgimport.h"
|
||||
|
||||
PYGAMEAPI_DEFINE_SLOTS(mask);
|
||||
|
||||
#define pgMask_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(mask, 0))
|
||||
#define pgMask_Check(x) ((x)->ob_type == &pgMask_Type)
|
||||
|
||||
#define import_pygame_mask() _IMPORT_PYGAME_MODULE(mask)
|
||||
|
||||
#endif /* ~PYGAMEAPI_MASK_INTERNAL */
|
||||
|
||||
#endif /* ~PGMASK_H */
|
||||
71
venv/include/site/python3.12/pygame/include/pygame_mixer.h
Normal file
71
venv/include/site/python3.12/pygame/include/pygame_mixer.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
#ifndef PGMIXER_H
|
||||
#define PGMIXER_H
|
||||
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
|
||||
#include "pgcompat.h"
|
||||
|
||||
struct Mix_Chunk;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD Mix_Chunk *chunk;
|
||||
Uint8 *mem;
|
||||
PyObject *weakreflist;
|
||||
} pgSoundObject;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD int chan;
|
||||
} pgChannelObject;
|
||||
|
||||
#define pgSound_AsChunk(x) (((pgSoundObject *)x)->chunk)
|
||||
#define pgChannel_AsInt(x) (((pgChannelObject *)x)->chan)
|
||||
|
||||
#include "pgimport.h"
|
||||
|
||||
#ifndef PYGAMEAPI_MIXER_INTERNAL
|
||||
|
||||
PYGAMEAPI_DEFINE_SLOTS(mixer);
|
||||
|
||||
#define pgSound_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(mixer, 0))
|
||||
|
||||
#define pgSound_Check(x) ((x)->ob_type == &pgSound_Type)
|
||||
|
||||
#define pgSound_New \
|
||||
(*(PyObject * (*)(Mix_Chunk *)) PYGAMEAPI_GET_SLOT(mixer, 1))
|
||||
|
||||
#define pgSound_Play \
|
||||
(*(PyObject * (*)(PyObject *, PyObject *)) PYGAMEAPI_GET_SLOT(mixer, 2))
|
||||
|
||||
#define pgChannel_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(mixer, 3))
|
||||
#define pgChannel_Check(x) ((x)->ob_type == &pgChannel_Type)
|
||||
|
||||
#define pgChannel_New (*(PyObject * (*)(int)) PYGAMEAPI_GET_SLOT(mixer, 4))
|
||||
|
||||
#define import_pygame_mixer() _IMPORT_PYGAME_MODULE(mixer)
|
||||
|
||||
#endif /* PYGAMEAPI_MIXER_INTERNAL */
|
||||
|
||||
#endif /* ~PGMIXER_H */
|
||||
6203
venv/include/site/python3.12/pygame/include/sse2neon.h
Normal file
6203
venv/include/site/python3.12/pygame/include/sse2neon.h
Normal file
File diff suppressed because it is too large
Load Diff
7
venv/include/site/python3.12/pygame/mask.h
Normal file
7
venv/include/site/python3.12/pygame/mask.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef PGMASK_INTERNAL_H
|
||||
#define PGMASK_INTERNAL_H
|
||||
|
||||
#include "include/pygame_mask.h"
|
||||
#define PYGAMEAPI_MASK_NUMSLOTS 1
|
||||
|
||||
#endif /* ~PGMASK_INTERNAL_H */
|
||||
14
venv/include/site/python3.12/pygame/mixer.h
Normal file
14
venv/include/site/python3.12/pygame/mixer.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef MIXER_INTERNAL_H
|
||||
#define MIXER_INTERNAL_H
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
|
||||
/* test mixer initializations */
|
||||
#define MIXER_INIT_CHECK() \
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) \
|
||||
return RAISE(pgExc_SDLError, "mixer not initialized")
|
||||
|
||||
#define PYGAMEAPI_MIXER_NUMSLOTS 5
|
||||
#include "include/pygame_mixer.h"
|
||||
|
||||
#endif /* ~MIXER_INTERNAL_H */
|
||||
123
venv/include/site/python3.12/pygame/palette.h
Normal file
123
venv/include/site/python3.12/pygame/palette.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
#ifndef PALETTE_H
|
||||
#define PALETTE_H
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
/* SDL 2 does not assign a default palette color scheme to a new 8 bit
|
||||
* surface. Instead, the palette is set all white. This defines the SDL 1.2
|
||||
* default palette.
|
||||
*/
|
||||
static const SDL_Color default_palette_colors[] = {
|
||||
{0, 0, 0, 255}, {0, 0, 85, 255}, {0, 0, 170, 255},
|
||||
{0, 0, 255, 255}, {0, 36, 0, 255}, {0, 36, 85, 255},
|
||||
{0, 36, 170, 255}, {0, 36, 255, 255}, {0, 73, 0, 255},
|
||||
{0, 73, 85, 255}, {0, 73, 170, 255}, {0, 73, 255, 255},
|
||||
{0, 109, 0, 255}, {0, 109, 85, 255}, {0, 109, 170, 255},
|
||||
{0, 109, 255, 255}, {0, 146, 0, 255}, {0, 146, 85, 255},
|
||||
{0, 146, 170, 255}, {0, 146, 255, 255}, {0, 182, 0, 255},
|
||||
{0, 182, 85, 255}, {0, 182, 170, 255}, {0, 182, 255, 255},
|
||||
{0, 219, 0, 255}, {0, 219, 85, 255}, {0, 219, 170, 255},
|
||||
{0, 219, 255, 255}, {0, 255, 0, 255}, {0, 255, 85, 255},
|
||||
{0, 255, 170, 255}, {0, 255, 255, 255}, {85, 0, 0, 255},
|
||||
{85, 0, 85, 255}, {85, 0, 170, 255}, {85, 0, 255, 255},
|
||||
{85, 36, 0, 255}, {85, 36, 85, 255}, {85, 36, 170, 255},
|
||||
{85, 36, 255, 255}, {85, 73, 0, 255}, {85, 73, 85, 255},
|
||||
{85, 73, 170, 255}, {85, 73, 255, 255}, {85, 109, 0, 255},
|
||||
{85, 109, 85, 255}, {85, 109, 170, 255}, {85, 109, 255, 255},
|
||||
{85, 146, 0, 255}, {85, 146, 85, 255}, {85, 146, 170, 255},
|
||||
{85, 146, 255, 255}, {85, 182, 0, 255}, {85, 182, 85, 255},
|
||||
{85, 182, 170, 255}, {85, 182, 255, 255}, {85, 219, 0, 255},
|
||||
{85, 219, 85, 255}, {85, 219, 170, 255}, {85, 219, 255, 255},
|
||||
{85, 255, 0, 255}, {85, 255, 85, 255}, {85, 255, 170, 255},
|
||||
{85, 255, 255, 255}, {170, 0, 0, 255}, {170, 0, 85, 255},
|
||||
{170, 0, 170, 255}, {170, 0, 255, 255}, {170, 36, 0, 255},
|
||||
{170, 36, 85, 255}, {170, 36, 170, 255}, {170, 36, 255, 255},
|
||||
{170, 73, 0, 255}, {170, 73, 85, 255}, {170, 73, 170, 255},
|
||||
{170, 73, 255, 255}, {170, 109, 0, 255}, {170, 109, 85, 255},
|
||||
{170, 109, 170, 255}, {170, 109, 255, 255}, {170, 146, 0, 255},
|
||||
{170, 146, 85, 255}, {170, 146, 170, 255}, {170, 146, 255, 255},
|
||||
{170, 182, 0, 255}, {170, 182, 85, 255}, {170, 182, 170, 255},
|
||||
{170, 182, 255, 255}, {170, 219, 0, 255}, {170, 219, 85, 255},
|
||||
{170, 219, 170, 255}, {170, 219, 255, 255}, {170, 255, 0, 255},
|
||||
{170, 255, 85, 255}, {170, 255, 170, 255}, {170, 255, 255, 255},
|
||||
{255, 0, 0, 255}, {255, 0, 85, 255}, {255, 0, 170, 255},
|
||||
{255, 0, 255, 255}, {255, 36, 0, 255}, {255, 36, 85, 255},
|
||||
{255, 36, 170, 255}, {255, 36, 255, 255}, {255, 73, 0, 255},
|
||||
{255, 73, 85, 255}, {255, 73, 170, 255}, {255, 73, 255, 255},
|
||||
{255, 109, 0, 255}, {255, 109, 85, 255}, {255, 109, 170, 255},
|
||||
{255, 109, 255, 255}, {255, 146, 0, 255}, {255, 146, 85, 255},
|
||||
{255, 146, 170, 255}, {255, 146, 255, 255}, {255, 182, 0, 255},
|
||||
{255, 182, 85, 255}, {255, 182, 170, 255}, {255, 182, 255, 255},
|
||||
{255, 219, 0, 255}, {255, 219, 85, 255}, {255, 219, 170, 255},
|
||||
{255, 219, 255, 255}, {255, 255, 0, 255}, {255, 255, 85, 255},
|
||||
{255, 255, 170, 255}, {255, 255, 255, 255}, {0, 0, 0, 255},
|
||||
{0, 0, 85, 255}, {0, 0, 170, 255}, {0, 0, 255, 255},
|
||||
{0, 36, 0, 255}, {0, 36, 85, 255}, {0, 36, 170, 255},
|
||||
{0, 36, 255, 255}, {0, 73, 0, 255}, {0, 73, 85, 255},
|
||||
{0, 73, 170, 255}, {0, 73, 255, 255}, {0, 109, 0, 255},
|
||||
{0, 109, 85, 255}, {0, 109, 170, 255}, {0, 109, 255, 255},
|
||||
{0, 146, 0, 255}, {0, 146, 85, 255}, {0, 146, 170, 255},
|
||||
{0, 146, 255, 255}, {0, 182, 0, 255}, {0, 182, 85, 255},
|
||||
{0, 182, 170, 255}, {0, 182, 255, 255}, {0, 219, 0, 255},
|
||||
{0, 219, 85, 255}, {0, 219, 170, 255}, {0, 219, 255, 255},
|
||||
{0, 255, 0, 255}, {0, 255, 85, 255}, {0, 255, 170, 255},
|
||||
{0, 255, 255, 255}, {85, 0, 0, 255}, {85, 0, 85, 255},
|
||||
{85, 0, 170, 255}, {85, 0, 255, 255}, {85, 36, 0, 255},
|
||||
{85, 36, 85, 255}, {85, 36, 170, 255}, {85, 36, 255, 255},
|
||||
{85, 73, 0, 255}, {85, 73, 85, 255}, {85, 73, 170, 255},
|
||||
{85, 73, 255, 255}, {85, 109, 0, 255}, {85, 109, 85, 255},
|
||||
{85, 109, 170, 255}, {85, 109, 255, 255}, {85, 146, 0, 255},
|
||||
{85, 146, 85, 255}, {85, 146, 170, 255}, {85, 146, 255, 255},
|
||||
{85, 182, 0, 255}, {85, 182, 85, 255}, {85, 182, 170, 255},
|
||||
{85, 182, 255, 255}, {85, 219, 0, 255}, {85, 219, 85, 255},
|
||||
{85, 219, 170, 255}, {85, 219, 255, 255}, {85, 255, 0, 255},
|
||||
{85, 255, 85, 255}, {85, 255, 170, 255}, {85, 255, 255, 255},
|
||||
{170, 0, 0, 255}, {170, 0, 85, 255}, {170, 0, 170, 255},
|
||||
{170, 0, 255, 255}, {170, 36, 0, 255}, {170, 36, 85, 255},
|
||||
{170, 36, 170, 255}, {170, 36, 255, 255}, {170, 73, 0, 255},
|
||||
{170, 73, 85, 255}, {170, 73, 170, 255}, {170, 73, 255, 255},
|
||||
{170, 109, 0, 255}, {170, 109, 85, 255}, {170, 109, 170, 255},
|
||||
{170, 109, 255, 255}, {170, 146, 0, 255}, {170, 146, 85, 255},
|
||||
{170, 146, 170, 255}, {170, 146, 255, 255}, {170, 182, 0, 255},
|
||||
{170, 182, 85, 255}, {170, 182, 170, 255}, {170, 182, 255, 255},
|
||||
{170, 219, 0, 255}, {170, 219, 85, 255}, {170, 219, 170, 255},
|
||||
{170, 219, 255, 255}, {170, 255, 0, 255}, {170, 255, 85, 255},
|
||||
{170, 255, 170, 255}, {170, 255, 255, 255}, {255, 0, 0, 255},
|
||||
{255, 0, 85, 255}, {255, 0, 170, 255}, {255, 0, 255, 255},
|
||||
{255, 36, 0, 255}, {255, 36, 85, 255}, {255, 36, 170, 255},
|
||||
{255, 36, 255, 255}, {255, 73, 0, 255}, {255, 73, 85, 255},
|
||||
{255, 73, 170, 255}, {255, 73, 255, 255}, {255, 109, 0, 255},
|
||||
{255, 109, 85, 255}, {255, 109, 170, 255}, {255, 109, 255, 255},
|
||||
{255, 146, 0, 255}, {255, 146, 85, 255}, {255, 146, 170, 255},
|
||||
{255, 146, 255, 255}, {255, 182, 0, 255}, {255, 182, 85, 255},
|
||||
{255, 182, 170, 255}, {255, 182, 255, 255}, {255, 219, 0, 255},
|
||||
{255, 219, 85, 255}, {255, 219, 170, 255}, {255, 219, 255, 255},
|
||||
{255, 255, 0, 255}, {255, 255, 85, 255}, {255, 255, 170, 255},
|
||||
{255, 255, 255, 255}};
|
||||
|
||||
static const int default_palette_size =
|
||||
(int)(sizeof(default_palette_colors) / sizeof(SDL_Color));
|
||||
|
||||
#endif
|
||||
26
venv/include/site/python3.12/pygame/pgarrinter.h
Normal file
26
venv/include/site/python3.12/pygame/pgarrinter.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* array structure interface version 3 declarations */
|
||||
|
||||
#if !defined(PG_ARRAYINTER_HEADER)
|
||||
#define PG_ARRAYINTER_HEADER
|
||||
|
||||
static const int PAI_CONTIGUOUS = 0x01;
|
||||
static const int PAI_FORTRAN = 0x02;
|
||||
static const int PAI_ALIGNED = 0x100;
|
||||
static const int PAI_NOTSWAPPED = 0x200;
|
||||
static const int PAI_WRITEABLE = 0x400;
|
||||
static const int PAI_ARR_HAS_DESCR = 0x800;
|
||||
|
||||
typedef struct {
|
||||
int two; /* contains the integer 2 -- simple sanity check */
|
||||
int nd; /* number of dimensions */
|
||||
char typekind; /* kind in array -- character code of typestr */
|
||||
int itemsize; /* size of each element */
|
||||
int flags; /* flags indicating how the data should be */
|
||||
/* interpreted */
|
||||
Py_intptr_t *shape; /* A length-nd array of shape information */
|
||||
Py_intptr_t *strides; /* A length-nd array of stride information */
|
||||
void *data; /* A pointer to the first element of the array */
|
||||
PyObject *descr; /* NULL or a data-description */
|
||||
} PyArrayInterface;
|
||||
|
||||
#endif
|
||||
7
venv/include/site/python3.12/pygame/pgbufferproxy.h
Normal file
7
venv/include/site/python3.12/pygame/pgbufferproxy.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef PG_BUFPROXY_INTERNAL_H
|
||||
#define PG_BUFPROXY_INTERNAL_H
|
||||
|
||||
#include "include/pygame_bufferproxy.h"
|
||||
#define PYGAMEAPI_BUFPROXY_NUMSLOTS 4
|
||||
|
||||
#endif /* ~PG_BUFPROXY_INTERNAL_H */
|
||||
27
venv/include/site/python3.12/pygame/pgcompat.h
Normal file
27
venv/include/site/python3.12/pygame/pgcompat.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* Python 2.x/3.x compatibility tools (internal)
|
||||
*/
|
||||
#ifndef PGCOMPAT_INTERNAL_H
|
||||
#define PGCOMPAT_INTERNAL_H
|
||||
|
||||
#include "include/pgcompat.h"
|
||||
|
||||
/* Module init function returns new module instance. */
|
||||
#define MODINIT_DEFINE(mod_name) PyMODINIT_FUNC PyInit_##mod_name(void)
|
||||
|
||||
/* Defaults for unicode file path encoding */
|
||||
#if defined(MS_WIN32)
|
||||
#define UNICODE_DEF_FS_ERROR "replace"
|
||||
#else
|
||||
#define UNICODE_DEF_FS_ERROR "surrogateescape"
|
||||
#endif
|
||||
|
||||
#define RELATIVE_MODULE(m) ("." m)
|
||||
|
||||
#ifndef Py_TPFLAGS_HAVE_NEWBUFFER
|
||||
#define Py_TPFLAGS_HAVE_NEWBUFFER 0
|
||||
#endif
|
||||
|
||||
#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \
|
||||
PySlice_GetIndicesEx(slice, length, start, stop, step, slicelength)
|
||||
|
||||
#endif /* ~PGCOMPAT_INTERNAL_H */
|
||||
20
venv/include/site/python3.12/pygame/pgopengl.h
Normal file
20
venv/include/site/python3.12/pygame/pgopengl.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#if !defined(PGOPENGL_H)
|
||||
#define PGOPENGL_H
|
||||
|
||||
/** This header includes definitions of Opengl functions as pointer types for
|
||||
** use with the SDL function SDL_GL_GetProcAddress.
|
||||
**/
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define GL_APIENTRY __stdcall
|
||||
#else
|
||||
#define GL_APIENTRY
|
||||
#endif
|
||||
|
||||
typedef void(GL_APIENTRY *GL_glReadPixels_Func)(int, int, int, int,
|
||||
unsigned int, unsigned int,
|
||||
void *);
|
||||
|
||||
typedef void(GL_APIENTRY *GL_glViewport_Func)(int, int, unsigned int,
|
||||
unsigned int);
|
||||
#endif
|
||||
23
venv/include/site/python3.12/pygame/pgplatform.h
Normal file
23
venv/include/site/python3.12/pygame/pgplatform.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* platform/compiler adjustments (internal) */
|
||||
#ifndef PG_PLATFORM_INTERNAL_H
|
||||
#define PG_PLATFORM_INTERNAL_H
|
||||
|
||||
#include "include/pgplatform.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef ABS
|
||||
#define ABS(a) (((a) < 0) ? -(a) : (a))
|
||||
#endif
|
||||
|
||||
/* warnings */
|
||||
#define PG_STRINGIZE_HELPER(x) #x
|
||||
#define PG_STRINGIZE(x) PG_STRINGIZE_HELPER(x)
|
||||
#define PG_WARN(desc) \
|
||||
message(__FILE__ "(" PG_STRINGIZE(__LINE__) "): WARNING: " #desc)
|
||||
|
||||
#endif /* ~PG_PLATFORM_INTERNAL_H */
|
||||
32
venv/include/site/python3.12/pygame/pygame.h
Normal file
32
venv/include/site/python3.12/pygame/pygame.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
/* This will use PYGAMEAPI_DEFINE_SLOTS instead
|
||||
* of PYGAMEAPI_EXTERN_SLOTS for base modules.
|
||||
*/
|
||||
#ifndef PYGAME_INTERNAL_H
|
||||
#define PYGAME_INTERNAL_H
|
||||
|
||||
#define PYGAME_H
|
||||
#include "_pygame.h"
|
||||
|
||||
#endif /* ~PYGAME_INTERNAL_H */
|
||||
147
venv/include/site/python3.12/pygame/scrap.h
Normal file
147
venv/include/site/python3.12/pygame/scrap.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen
|
||||
|
||||
Originally put in the public domain by Sam Lantinga.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef SCRAP_H
|
||||
#define SCRAP_H
|
||||
|
||||
/* This is unconditionally defined in Python.h */
|
||||
#if defined(_POSIX_C_SOURCE)
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
/* Handle clipboard text and data in arbitrary formats */
|
||||
|
||||
/**
|
||||
* Predefined supported pygame scrap types.
|
||||
*/
|
||||
#define PYGAME_SCRAP_TEXT "text/plain"
|
||||
#define PYGAME_SCRAP_BMP "image/bmp"
|
||||
#define PYGAME_SCRAP_PPM "image/ppm"
|
||||
#define PYGAME_SCRAP_PBM "image/pbm"
|
||||
|
||||
/**
|
||||
* The supported scrap clipboard types.
|
||||
*
|
||||
* This is only relevant in a X11 environment, which supports mouse
|
||||
* selections as well. For Win32 and MacOS environments the default
|
||||
* clipboard is used, no matter what value is passed.
|
||||
*/
|
||||
typedef enum {
|
||||
SCRAP_CLIPBOARD,
|
||||
SCRAP_SELECTION /* only supported in X11 environments. */
|
||||
} ScrapClipType;
|
||||
|
||||
/**
|
||||
* Macro for initialization checks.
|
||||
*/
|
||||
#define PYGAME_SCRAP_INIT_CHECK() \
|
||||
if (!pygame_scrap_initialized()) \
|
||||
return (PyErr_SetString(pgExc_SDLError, "scrap system not initialized."), \
|
||||
NULL)
|
||||
|
||||
/**
|
||||
* \brief Checks, whether the pygame scrap module was initialized.
|
||||
*
|
||||
* \return 1 if the modules was initialized, 0 otherwise.
|
||||
*/
|
||||
extern int
|
||||
pygame_scrap_initialized(void);
|
||||
|
||||
/**
|
||||
* \brief Initializes the pygame scrap module internals. Call this before any
|
||||
* other method.
|
||||
*
|
||||
* \return 1 on successful initialization, 0 otherwise.
|
||||
*/
|
||||
extern int
|
||||
pygame_scrap_init(void);
|
||||
|
||||
/**
|
||||
* \brief Checks, whether the pygame window lost the clipboard focus or not.
|
||||
*
|
||||
* \return 1 if the window lost the focus, 0 otherwise.
|
||||
*/
|
||||
extern int
|
||||
pygame_scrap_lost(void);
|
||||
|
||||
/**
|
||||
* \brief Places content of a specific type into the clipboard.
|
||||
*
|
||||
* \note For X11 the following notes are important: The following types
|
||||
* are reserved for internal usage and thus will throw an error on
|
||||
* setting them: "TIMESTAMP", "TARGETS", "SDL_SELECTION".
|
||||
* Setting PYGAME_SCRAP_TEXT ("text/plain") will also automatically
|
||||
* set the X11 types "STRING" (XA_STRING), "TEXT" and "UTF8_STRING".
|
||||
*
|
||||
* For Win32 the following notes are important: Setting
|
||||
* PYGAME_SCRAP_TEXT ("text/plain") will also automatically set
|
||||
* the Win32 type "TEXT" (CF_TEXT).
|
||||
*
|
||||
* For QNX the following notes are important: Setting
|
||||
* PYGAME_SCRAP_TEXT ("text/plain") will also automatically set
|
||||
* the QNX type "TEXT" (Ph_CL_TEXT).
|
||||
*
|
||||
* \param type The type of the content.
|
||||
* \param srclen The length of the content.
|
||||
* \param src The NULL terminated content.
|
||||
* \return 1, if the content could be successfully pasted into the clipboard,
|
||||
* 0 otherwise.
|
||||
*/
|
||||
extern int
|
||||
pygame_scrap_put(char *type, Py_ssize_t srclen, char *src);
|
||||
|
||||
/**
|
||||
* \brief Gets the current content from the clipboard.
|
||||
*
|
||||
* \note The received content does not need to be the content previously
|
||||
* placed in the clipboard using pygame_put_scrap(). See the
|
||||
* pygame_put_scrap() notes for more details.
|
||||
*
|
||||
* \param type The type of the content to receive.
|
||||
* \param count The size of the returned content.
|
||||
* \return The content or NULL in case of an error or if no content of the
|
||||
* specified type was available.
|
||||
*/
|
||||
extern char *
|
||||
pygame_scrap_get(char *type, size_t *count);
|
||||
|
||||
/**
|
||||
* \brief Gets the currently available content types from the clipboard.
|
||||
*
|
||||
* \return The different available content types or NULL in case of an
|
||||
* error or if no content type is available.
|
||||
*/
|
||||
extern char **
|
||||
pygame_scrap_get_types(void);
|
||||
|
||||
/**
|
||||
* \brief Checks whether content for the specified scrap type is currently
|
||||
* available in the clipboard.
|
||||
*
|
||||
* \param type The type to check for.
|
||||
* \return 1, if there is content and 0 otherwise.
|
||||
*/
|
||||
extern int
|
||||
pygame_scrap_contains(char *type);
|
||||
|
||||
#endif /* SCRAP_H */
|
||||
84
venv/include/site/python3.12/pygame/simd_blitters.h
Normal file
84
venv/include/site/python3.12/pygame/simd_blitters.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#define NO_PYGAME_C_API
|
||||
#include "_surface.h"
|
||||
#include "_blit_info.h"
|
||||
|
||||
#if !defined(PG_ENABLE_ARM_NEON) && defined(__aarch64__)
|
||||
// arm64 has neon optimisations enabled by default, even when fpu=neon is not
|
||||
// passed
|
||||
#define PG_ENABLE_ARM_NEON 1
|
||||
#endif
|
||||
|
||||
int
|
||||
pg_sse2_at_runtime_but_uncompiled();
|
||||
int
|
||||
pg_neon_at_runtime_but_uncompiled();
|
||||
int
|
||||
pg_avx2_at_runtime_but_uncompiled();
|
||||
|
||||
#if (defined(__SSE2__) || defined(PG_ENABLE_ARM_NEON))
|
||||
void
|
||||
alphablit_alpha_sse2_argb_surf_alpha(SDL_BlitInfo *info);
|
||||
void
|
||||
alphablit_alpha_sse2_argb_no_surf_alpha(SDL_BlitInfo *info);
|
||||
void
|
||||
alphablit_alpha_sse2_argb_no_surf_alpha_opaque_dst(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_mul_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_mul_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_add_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_add_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_sub_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_sub_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_max_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_max_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_min_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_min_sse2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_premultiplied_sse2(SDL_BlitInfo *info);
|
||||
#endif /* (defined(__SSE2__) || defined(PG_ENABLE_ARM_NEON)) */
|
||||
|
||||
/* Deliberately putting these outside of the preprocessor guards as I want to
|
||||
move to a system of trusting the runtime checks to head to the right
|
||||
function and having a fallback function there if pygame is not compiled
|
||||
with the right stuff (this is the strategy used for AVX2 right now.
|
||||
Potentially I might want to shift both these into a slightly different
|
||||
file as they are not exactly blits (though v. similar) - or I could rename
|
||||
the SIMD trilogy of files to replace the word blit with something more
|
||||
generic like surface_ops*/
|
||||
|
||||
void
|
||||
premul_surf_color_by_alpha_non_simd(SDL_Surface *src, SDL_Surface *dst);
|
||||
void
|
||||
premul_surf_color_by_alpha_sse2(SDL_Surface *src, SDL_Surface *dst);
|
||||
|
||||
int
|
||||
pg_has_avx2();
|
||||
void
|
||||
blit_blend_rgba_mul_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_mul_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_add_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_add_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_sub_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_sub_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_max_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_max_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgba_min_avx2(SDL_BlitInfo *info);
|
||||
void
|
||||
blit_blend_rgb_min_avx2(SDL_BlitInfo *info);
|
||||
361
venv/include/site/python3.12/pygame/surface.h
Normal file
361
venv/include/site/python3.12/pygame/surface.h
Normal file
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
pygame - Python Game Library
|
||||
Copyright (C) 2000-2001 Pete Shinners
|
||||
Copyright (C) 2007 Marcus von Appen
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Pete Shinners
|
||||
pete@shinners.org
|
||||
*/
|
||||
|
||||
#ifndef SURFACE_H
|
||||
#define SURFACE_H
|
||||
|
||||
/* This is defined in SDL.h */
|
||||
#if defined(_POSIX_C_SOURCE)
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
#include "pygame.h"
|
||||
|
||||
/* Blend modes */
|
||||
#define PYGAME_BLEND_ADD 0x1
|
||||
#define PYGAME_BLEND_SUB 0x2
|
||||
#define PYGAME_BLEND_MULT 0x3
|
||||
#define PYGAME_BLEND_MIN 0x4
|
||||
#define PYGAME_BLEND_MAX 0x5
|
||||
|
||||
#define PYGAME_BLEND_RGB_ADD 0x1
|
||||
#define PYGAME_BLEND_RGB_SUB 0x2
|
||||
#define PYGAME_BLEND_RGB_MULT 0x3
|
||||
#define PYGAME_BLEND_RGB_MIN 0x4
|
||||
#define PYGAME_BLEND_RGB_MAX 0x5
|
||||
|
||||
#define PYGAME_BLEND_RGBA_ADD 0x6
|
||||
#define PYGAME_BLEND_RGBA_SUB 0x7
|
||||
#define PYGAME_BLEND_RGBA_MULT 0x8
|
||||
#define PYGAME_BLEND_RGBA_MIN 0x9
|
||||
#define PYGAME_BLEND_RGBA_MAX 0x10
|
||||
#define PYGAME_BLEND_PREMULTIPLIED 0x11
|
||||
#define PYGAME_BLEND_ALPHA_SDL2 0x12
|
||||
|
||||
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
||||
#define GET_PIXEL_24(b) (b[0] + (b[1] << 8) + (b[2] << 16))
|
||||
#else
|
||||
#define GET_PIXEL_24(b) (b[2] + (b[1] << 8) + (b[0] << 16))
|
||||
#endif
|
||||
|
||||
#define GET_PIXEL(pxl, bpp, source) \
|
||||
switch (bpp) { \
|
||||
case 2: \
|
||||
pxl = *((Uint16 *)(source)); \
|
||||
break; \
|
||||
case 4: \
|
||||
pxl = *((Uint32 *)(source)); \
|
||||
break; \
|
||||
default: { \
|
||||
Uint8 *b = (Uint8 *)source; \
|
||||
pxl = GET_PIXEL_24(b); \
|
||||
} break; \
|
||||
}
|
||||
|
||||
#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa) \
|
||||
SDL_GetRGBA(px, fmt, &(_sR), &(_sG), &(_sB), &(_sA)); \
|
||||
if (!ppa) { \
|
||||
_sA = 255; \
|
||||
}
|
||||
|
||||
#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt) \
|
||||
sr = _fmt->palette->colors[*((Uint8 *)(_src))].r; \
|
||||
sg = _fmt->palette->colors[*((Uint8 *)(_src))].g; \
|
||||
sb = _fmt->palette->colors[*((Uint8 *)(_src))].b; \
|
||||
sa = 255;
|
||||
|
||||
/* For 1 byte palette pixels */
|
||||
#define SET_PIXELVAL(px, fmt, _dR, _dG, _dB, _dA) \
|
||||
*(px) = (Uint8)SDL_MapRGBA(fmt, _dR, _dG, _dB, _dA)
|
||||
|
||||
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
||||
#define SET_OFFSETS_24(or, og, ob, fmt) \
|
||||
{ \
|
||||
or = (fmt->Rshift == 0 ? 0 : fmt->Rshift == 8 ? 1 : 2); \
|
||||
og = (fmt->Gshift == 0 ? 0 : fmt->Gshift == 8 ? 1 : 2); \
|
||||
ob = (fmt->Bshift == 0 ? 0 : fmt->Bshift == 8 ? 1 : 2); \
|
||||
}
|
||||
|
||||
#define SET_OFFSETS_32(or, og, ob, fmt) \
|
||||
{ \
|
||||
or = (fmt->Rshift == 0 ? 0 \
|
||||
: fmt->Rshift == 8 ? 1 \
|
||||
: fmt->Rshift == 16 ? 2 \
|
||||
: 3); \
|
||||
og = (fmt->Gshift == 0 ? 0 \
|
||||
: fmt->Gshift == 8 ? 1 \
|
||||
: fmt->Gshift == 16 ? 2 \
|
||||
: 3); \
|
||||
ob = (fmt->Bshift == 0 ? 0 \
|
||||
: fmt->Bshift == 8 ? 1 \
|
||||
: fmt->Bshift == 16 ? 2 \
|
||||
: 3); \
|
||||
}
|
||||
#else
|
||||
#define SET_OFFSETS_24(or, og, ob, fmt) \
|
||||
{ \
|
||||
or = (fmt->Rshift == 0 ? 2 : fmt->Rshift == 8 ? 1 : 0); \
|
||||
og = (fmt->Gshift == 0 ? 2 : fmt->Gshift == 8 ? 1 : 0); \
|
||||
ob = (fmt->Bshift == 0 ? 2 : fmt->Bshift == 8 ? 1 : 0); \
|
||||
}
|
||||
|
||||
#define SET_OFFSETS_32(or, og, ob, fmt) \
|
||||
{ \
|
||||
or = (fmt->Rshift == 0 ? 3 \
|
||||
: fmt->Rshift == 8 ? 2 \
|
||||
: fmt->Rshift == 16 ? 1 \
|
||||
: 0); \
|
||||
og = (fmt->Gshift == 0 ? 3 \
|
||||
: fmt->Gshift == 8 ? 2 \
|
||||
: fmt->Gshift == 16 ? 1 \
|
||||
: 0); \
|
||||
ob = (fmt->Bshift == 0 ? 3 \
|
||||
: fmt->Bshift == 8 ? 2 \
|
||||
: fmt->Bshift == 16 ? 1 \
|
||||
: 0); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CREATE_PIXEL(buf, r, g, b, a, bp, ft) \
|
||||
switch (bp) { \
|
||||
case 2: \
|
||||
*((Uint16 *)(buf)) = ((r >> ft->Rloss) << ft->Rshift) | \
|
||||
((g >> ft->Gloss) << ft->Gshift) | \
|
||||
((b >> ft->Bloss) << ft->Bshift) | \
|
||||
((a >> ft->Aloss) << ft->Ashift); \
|
||||
break; \
|
||||
case 4: \
|
||||
*((Uint32 *)(buf)) = ((r >> ft->Rloss) << ft->Rshift) | \
|
||||
((g >> ft->Gloss) << ft->Gshift) | \
|
||||
((b >> ft->Bloss) << ft->Bshift) | \
|
||||
((a >> ft->Aloss) << ft->Ashift); \
|
||||
break; \
|
||||
}
|
||||
|
||||
/* Pretty good idea from Tom Duff :-). */
|
||||
#define LOOP_UNROLLED4(code, n, width) \
|
||||
n = (width + 3) / 4; \
|
||||
switch (width & 3) { \
|
||||
case 0: \
|
||||
do { \
|
||||
code; \
|
||||
case 3: \
|
||||
code; \
|
||||
case 2: \
|
||||
code; \
|
||||
case 1: \
|
||||
code; \
|
||||
} while (--n > 0); \
|
||||
}
|
||||
|
||||
/* Used in the srcbpp == dstbpp == 1 blend functions */
|
||||
#define REPEAT_3(code) \
|
||||
code; \
|
||||
code; \
|
||||
code;
|
||||
|
||||
#define REPEAT_4(code) \
|
||||
code; \
|
||||
code; \
|
||||
code; \
|
||||
code;
|
||||
|
||||
#define BLEND_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
tmp = dR + sR; \
|
||||
dR = (tmp <= 255 ? tmp : 255); \
|
||||
tmp = dG + sG; \
|
||||
dG = (tmp <= 255 ? tmp : 255); \
|
||||
tmp = dB + sB; \
|
||||
dB = (tmp <= 255 ? tmp : 255);
|
||||
|
||||
#define BLEND_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
tmp = dR - sR; \
|
||||
dR = (tmp >= 0 ? tmp : 0); \
|
||||
tmp = dG - sG; \
|
||||
dG = (tmp >= 0 ? tmp : 0); \
|
||||
tmp = dB - sB; \
|
||||
dB = (tmp >= 0 ? tmp : 0);
|
||||
|
||||
#define BLEND_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
dR = (dR && sR) ? ((dR * sR) + 255) >> 8 : 0; \
|
||||
dG = (dG && sG) ? ((dG * sG) + 255) >> 8 : 0; \
|
||||
dB = (dB && sB) ? ((dB * sB) + 255) >> 8 : 0;
|
||||
|
||||
#define BLEND_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
if (sR < dR) { \
|
||||
dR = sR; \
|
||||
} \
|
||||
if (sG < dG) { \
|
||||
dG = sG; \
|
||||
} \
|
||||
if (sB < dB) { \
|
||||
dB = sB; \
|
||||
}
|
||||
|
||||
#define BLEND_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
if (sR > dR) { \
|
||||
dR = sR; \
|
||||
} \
|
||||
if (sG > dG) { \
|
||||
dG = sG; \
|
||||
} \
|
||||
if (sB > dB) { \
|
||||
dB = sB; \
|
||||
}
|
||||
|
||||
#define BLEND_RGBA_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
tmp = dR + sR; \
|
||||
dR = (tmp <= 255 ? tmp : 255); \
|
||||
tmp = dG + sG; \
|
||||
dG = (tmp <= 255 ? tmp : 255); \
|
||||
tmp = dB + sB; \
|
||||
dB = (tmp <= 255 ? tmp : 255); \
|
||||
tmp = dA + sA; \
|
||||
dA = (tmp <= 255 ? tmp : 255);
|
||||
|
||||
#define BLEND_RGBA_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
tmp = dR - sR; \
|
||||
dR = (tmp >= 0 ? tmp : 0); \
|
||||
tmp = dG - sG; \
|
||||
dG = (tmp >= 0 ? tmp : 0); \
|
||||
tmp = dB - sB; \
|
||||
dB = (tmp >= 0 ? tmp : 0); \
|
||||
tmp = dA - sA; \
|
||||
dA = (tmp >= 0 ? tmp : 0);
|
||||
|
||||
#define BLEND_RGBA_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
dR = (dR && sR) ? ((dR * sR) + 255) >> 8 : 0; \
|
||||
dG = (dG && sG) ? ((dG * sG) + 255) >> 8 : 0; \
|
||||
dB = (dB && sB) ? ((dB * sB) + 255) >> 8 : 0; \
|
||||
dA = (dA && sA) ? ((dA * sA) + 255) >> 8 : 0;
|
||||
|
||||
#define BLEND_RGBA_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
if (sR < dR) { \
|
||||
dR = sR; \
|
||||
} \
|
||||
if (sG < dG) { \
|
||||
dG = sG; \
|
||||
} \
|
||||
if (sB < dB) { \
|
||||
dB = sB; \
|
||||
} \
|
||||
if (sA < dA) { \
|
||||
dA = sA; \
|
||||
}
|
||||
|
||||
#define BLEND_RGBA_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
if (sR > dR) { \
|
||||
dR = sR; \
|
||||
} \
|
||||
if (sG > dG) { \
|
||||
dG = sG; \
|
||||
} \
|
||||
if (sB > dB) { \
|
||||
dB = sB; \
|
||||
} \
|
||||
if (sA > dA) { \
|
||||
dA = sA; \
|
||||
}
|
||||
|
||||
#if 1
|
||||
/* Choose an alpha blend equation. If the sign is preserved on a right shift
|
||||
* then use a specialized, faster, equation. Otherwise a more general form,
|
||||
* where all additions are done before the shift, is needed.
|
||||
*/
|
||||
#if (-1 >> 1) < 0
|
||||
#define ALPHA_BLEND_COMP(sC, dC, sA) ((((sC - dC) * sA + sC) >> 8) + dC)
|
||||
#else
|
||||
#define ALPHA_BLEND_COMP(sC, dC, sA) (((dC << 8) + (sC - dC) * sA + sC) >> 8)
|
||||
#endif
|
||||
|
||||
#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
do { \
|
||||
if (dA) { \
|
||||
dR = ALPHA_BLEND_COMP(sR, dR, sA); \
|
||||
dG = ALPHA_BLEND_COMP(sG, dG, sA); \
|
||||
dB = ALPHA_BLEND_COMP(sB, dB, sA); \
|
||||
dA = sA + dA - ((sA * dA) / 255); \
|
||||
} \
|
||||
else { \
|
||||
dR = sR; \
|
||||
dG = sG; \
|
||||
dB = sB; \
|
||||
dA = sA; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ALPHA_BLEND_PREMULTIPLIED_COMP(sC, dC, sA) \
|
||||
(sC + dC - ((dC + 1) * sA >> 8))
|
||||
|
||||
#define ALPHA_BLEND_PREMULTIPLIED(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
do { \
|
||||
dR = ALPHA_BLEND_PREMULTIPLIED_COMP(sR, dR, sA); \
|
||||
dG = ALPHA_BLEND_PREMULTIPLIED_COMP(sG, dG, sA); \
|
||||
dB = ALPHA_BLEND_PREMULTIPLIED_COMP(sB, dB, sA); \
|
||||
dA = ALPHA_BLEND_PREMULTIPLIED_COMP(sA, dA, sA); \
|
||||
} while (0)
|
||||
#elif 0
|
||||
|
||||
#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \
|
||||
do { \
|
||||
if (sA) { \
|
||||
if (dA && sA < 255) { \
|
||||
int dContrib = dA * (255 - sA) / 255; \
|
||||
dA = sA + dA - ((sA * dA) / 255); \
|
||||
dR = (dR * dContrib + sR * sA) / dA; \
|
||||
dG = (dG * dContrib + sG * sA) / dA; \
|
||||
dB = (dB * dContrib + sB * sA) / dA; \
|
||||
} \
|
||||
else { \
|
||||
dR = sR; \
|
||||
dG = sG; \
|
||||
dB = sB; \
|
||||
dA = sA; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
int
|
||||
surface_fill_blend(SDL_Surface *surface, SDL_Rect *rect, Uint32 color,
|
||||
int blendargs);
|
||||
|
||||
void
|
||||
surface_respect_clip_rect(SDL_Surface *surface, SDL_Rect *rect);
|
||||
|
||||
int
|
||||
pygame_AlphaBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst,
|
||||
SDL_Rect *dstrect, int the_args);
|
||||
|
||||
int
|
||||
pygame_Blit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst,
|
||||
SDL_Rect *dstrect, int the_args);
|
||||
|
||||
int
|
||||
premul_surf_color_by_alpha(SDL_Surface *src, SDL_Surface *dst);
|
||||
|
||||
int
|
||||
pg_warn_simd_at_runtime_but_uncompiled();
|
||||
|
||||
#endif /* SURFACE_H */
|
||||
44
venv/lib/python3.12/site-packages/PyInstaller/__init__.py
Normal file
44
venv/lib/python3.12/site-packages/PyInstaller/__init__.py
Normal file
@@ -0,0 +1,44 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
__all__ = ('HOMEPATH', 'PLATFORM', '__version__', 'DEFAULT_DISTPATH', 'DEFAULT_SPECPATH', 'DEFAULT_WORKPATH')
|
||||
|
||||
import os
|
||||
|
||||
from PyInstaller import compat
|
||||
|
||||
# Note: Keep this variable as plain string so it could be updated automatically when doing a release.
|
||||
__version__ = '6.21.0'
|
||||
|
||||
# Absolute path of this package's directory. Save this early so all submodules can use the absolute path. This is
|
||||
# required for example if the current directory changes prior to loading the hooks.
|
||||
PACKAGEPATH = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
HOMEPATH = os.path.dirname(PACKAGEPATH)
|
||||
|
||||
# Default values of paths where to put files created by PyInstaller. If changing these, do not forget to update the
|
||||
# help text for corresponding command-line options, defined in build_main.
|
||||
|
||||
# Where to put created .spec file.
|
||||
DEFAULT_SPECPATH = os.getcwd()
|
||||
# Where to put the final frozen application.
|
||||
DEFAULT_DISTPATH = os.path.join(os.getcwd(), 'dist')
|
||||
# Where to put all the temporary files; .log, .pyz, etc.
|
||||
DEFAULT_WORKPATH = os.path.join(os.getcwd(), 'build')
|
||||
|
||||
PLATFORM = compat.system + '-' + compat.architecture
|
||||
# Include machine name in path to bootloader for some machines (e.g., 'arm'). Explicitly avoid doing this on macOS,
|
||||
# where we keep universal2 bootloaders in Darwin-64bit folder regardless of whether we are on x86_64 or arm64.
|
||||
if compat.machine and not compat.is_darwin:
|
||||
PLATFORM += '-' + compat.machine
|
||||
# Similarly, disambiguate musl Linux from glibc Linux.
|
||||
if compat.is_musl:
|
||||
PLATFORM += '-musl'
|
||||
321
venv/lib/python3.12/site-packages/PyInstaller/__main__.py
Normal file
321
venv/lib/python3.12/site-packages/PyInstaller/__main__.py
Normal file
@@ -0,0 +1,321 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Main command-line interface to PyInstaller.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import pathlib
|
||||
from collections import defaultdict
|
||||
|
||||
from PyInstaller import __version__
|
||||
from PyInstaller import log as logging
|
||||
# Note: do not import anything else until compat.check_requirements function is run!
|
||||
from PyInstaller import compat
|
||||
|
||||
try:
|
||||
from argcomplete import autocomplete
|
||||
except ImportError:
|
||||
|
||||
def autocomplete(parser):
|
||||
return None
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Taken from https://stackoverflow.com/a/22157136 to format args more flexibly: any help text which beings with ``R|``
|
||||
# will have all newlines preserved; the help text will be line wrapped. See
|
||||
# https://docs.python.org/3/library/argparse.html#formatter-class.
|
||||
|
||||
|
||||
# This is used by the ``--debug`` option.
|
||||
class _SmartFormatter(argparse.HelpFormatter):
|
||||
def _split_lines(self, text, width):
|
||||
if text.startswith('R|'):
|
||||
# The underlying implementation of ``RawTextHelpFormatter._split_lines`` invokes this; mimic it.
|
||||
return text[2:].splitlines()
|
||||
else:
|
||||
# Invoke the usual formatter.
|
||||
return super()._split_lines(text, width)
|
||||
|
||||
|
||||
def run_makespec(filenames, **opts):
|
||||
# Split pathex by using the path separator
|
||||
temppaths = opts['pathex'][:]
|
||||
pathex = opts['pathex'] = []
|
||||
for p in temppaths:
|
||||
pathex.extend(p.split(os.pathsep))
|
||||
|
||||
import PyInstaller.building.makespec
|
||||
|
||||
spec_file = PyInstaller.building.makespec.main(filenames, **opts)
|
||||
logger.info('wrote %s' % spec_file)
|
||||
return spec_file
|
||||
|
||||
|
||||
def run_build(pyi_config, spec_file, **kwargs):
|
||||
import PyInstaller.building.build_main
|
||||
PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
|
||||
|
||||
|
||||
def __add_options(parser):
|
||||
parser.add_argument(
|
||||
'-v',
|
||||
'--version',
|
||||
action='version',
|
||||
version=__version__,
|
||||
help='Show program version info and exit.',
|
||||
)
|
||||
|
||||
|
||||
class _PyiArgumentParser(argparse.ArgumentParser):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._pyi_action_groups = defaultdict(list)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def _add_options(self, __add_options: callable, name: str = ""):
|
||||
"""
|
||||
Mutate self with the given callable, storing any new actions added in a named group
|
||||
"""
|
||||
n_actions_before = len(getattr(self, "_actions", []))
|
||||
__add_options(self) # preserves old behavior
|
||||
new_actions = getattr(self, "_actions", [])[n_actions_before:]
|
||||
self._pyi_action_groups[name].extend(new_actions)
|
||||
|
||||
def _option_name(self, action):
|
||||
"""
|
||||
Get the option name(s) associated with an action
|
||||
|
||||
For options that define both short and long names, this function will
|
||||
return the long names joined by "/"
|
||||
"""
|
||||
longnames = [name for name in action.option_strings if name.startswith("--")]
|
||||
if longnames:
|
||||
name = "/".join(longnames)
|
||||
else:
|
||||
name = action.option_strings[0]
|
||||
return name
|
||||
|
||||
def _forbid_options(self, args: argparse.Namespace, group: str, errmsg: str = ""):
|
||||
"""Forbid options from a named action group"""
|
||||
options = defaultdict(str)
|
||||
for action in self._pyi_action_groups[group]:
|
||||
dest = action.dest
|
||||
name = self._option_name(action)
|
||||
if getattr(args, dest) is not self.get_default(dest):
|
||||
if dest in options:
|
||||
options[dest] += "/"
|
||||
options[dest] += name
|
||||
|
||||
# if any options from the forbidden group are not the default values,
|
||||
# the user must have passed them in, so issue an error report
|
||||
if options:
|
||||
sep = "\n "
|
||||
bad = sep.join(options.values())
|
||||
if errmsg:
|
||||
errmsg = "\n" + errmsg
|
||||
raise SystemExit(f"ERROR: option(s) not allowed:{sep}{bad}{errmsg}")
|
||||
|
||||
|
||||
def generate_parser() -> _PyiArgumentParser:
|
||||
"""
|
||||
Build an argparse parser for PyInstaller's main CLI.
|
||||
"""
|
||||
|
||||
import PyInstaller.building.build_main
|
||||
import PyInstaller.building.makespec
|
||||
import PyInstaller.log
|
||||
|
||||
parser = _PyiArgumentParser(formatter_class=_SmartFormatter)
|
||||
parser.prog = "pyinstaller"
|
||||
|
||||
parser._add_options(__add_options)
|
||||
parser._add_options(PyInstaller.building.makespec.__add_options, name="makespec")
|
||||
parser._add_options(PyInstaller.building.build_main.__add_options, name="build_main")
|
||||
parser._add_options(PyInstaller.log.__add_options, name="log")
|
||||
|
||||
parser.add_argument(
|
||||
'filenames',
|
||||
metavar='scriptname',
|
||||
nargs='+',
|
||||
help="Name of scriptfiles to be processed or exactly one .spec file. If a .spec file is specified, most "
|
||||
"options are unnecessary and are ignored.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def run(pyi_args: list | None = None, pyi_config: dict | None = None):
|
||||
"""
|
||||
pyi_args allows running PyInstaller programmatically without a subprocess
|
||||
pyi_config allows checking configuration once when running multiple tests
|
||||
"""
|
||||
compat.check_requirements()
|
||||
check_unsafe_privileges()
|
||||
|
||||
import PyInstaller.log
|
||||
|
||||
old_sys_argv = sys.argv
|
||||
try:
|
||||
parser = generate_parser()
|
||||
autocomplete(parser)
|
||||
if pyi_args is None:
|
||||
pyi_args = sys.argv[1:]
|
||||
try:
|
||||
index = pyi_args.index("--")
|
||||
except ValueError:
|
||||
index = len(pyi_args)
|
||||
args = parser.parse_args(pyi_args[:index])
|
||||
spec_args = pyi_args[index + 1:]
|
||||
PyInstaller.log.__process_options(parser, args)
|
||||
|
||||
# Print PyInstaller version, Python version, and platform as the first line to stdout. This helps us identify
|
||||
# PyInstaller, Python, and platform version when users report issues.
|
||||
try:
|
||||
from _pyinstaller_hooks_contrib import __version__ as contrib_hooks_version
|
||||
except Exception:
|
||||
contrib_hooks_version = 'unknown'
|
||||
|
||||
logger.info('PyInstaller: %s, contrib hooks: %s', __version__, contrib_hooks_version)
|
||||
logger.info('Python: %s%s', platform.python_version(), " (conda)" if compat.is_conda else "")
|
||||
logger.info('Platform: %s', platform.platform())
|
||||
logger.info('Python environment: %s', sys.prefix)
|
||||
|
||||
# Skip creating .spec when .spec file is supplied.
|
||||
if args.filenames[0].endswith('.spec'):
|
||||
parser._forbid_options(
|
||||
args, group="makespec", errmsg="makespec options not valid when a .spec file is given"
|
||||
)
|
||||
spec_file = args.filenames[0]
|
||||
else:
|
||||
# Ensure that the given script files exist, before trying to generate the .spec file.
|
||||
# This prevents us from overwriting an existing (and customized) .spec file if user makes a typo in the
|
||||
# .spec file's suffix when trying to build it, for example, `pyinstaller program.cpes` (see #8276).
|
||||
# It also prevents creation of a .spec file when `pyinstaller program.py` is accidentally ran from a
|
||||
# directory that does not contain the script (for example, due to failing to change the directory prior
|
||||
# to running the command).
|
||||
for filename in args.filenames:
|
||||
if not os.path.isfile(filename):
|
||||
raise SystemExit(f"ERROR: Script file {filename!r} does not exist.")
|
||||
spec_file = run_makespec(**vars(args))
|
||||
|
||||
sys.argv = [spec_file, *spec_args]
|
||||
run_build(pyi_config, spec_file, **vars(args))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
raise SystemExit("Aborted by user request.")
|
||||
except RecursionError:
|
||||
from PyInstaller import _recursion_too_deep_message
|
||||
_recursion_too_deep_message.raise_with_msg()
|
||||
finally:
|
||||
sys.argv = old_sys_argv
|
||||
|
||||
|
||||
def _console_script_run():
|
||||
# Python prepends the main script's parent directory to sys.path. When PyInstaller is ran via the usual
|
||||
# `pyinstaller` CLI entry point, this directory is $pythonprefix/bin which should not be in sys.path.
|
||||
if os.path.basename(sys.path[0]) in ("bin", "Scripts"):
|
||||
sys.path.pop(0)
|
||||
run()
|
||||
|
||||
|
||||
def check_unsafe_privileges():
|
||||
"""
|
||||
Forbid dangerous usage of PyInstaller with escalated privileges
|
||||
"""
|
||||
if compat.is_win and not compat.is_win_wine:
|
||||
# Discourage (with the intention to eventually block) people using *run as admin* with PyInstaller.
|
||||
# There are 4 cases, block case 3 but be careful not to also block case 2.
|
||||
# 1. User has no admin access: TokenElevationTypeDefault
|
||||
# 2. User is an admin/UAC disabled (common on CI/VMs): TokenElevationTypeDefault
|
||||
# 3. User has used *run as administrator* to elevate: TokenElevationTypeFull
|
||||
# 4. User can escalate but hasn't: TokenElevationTypeLimited
|
||||
# https://techcommunity.microsoft.com/t5/windows-blog-archive/how-to-determine-if-a-user-is-a-member-of-the-administrators/ba-p/228476
|
||||
import ctypes
|
||||
|
||||
advapi32 = ctypes.CDLL("Advapi32.dll")
|
||||
kernel32 = ctypes.CDLL("kernel32.dll")
|
||||
|
||||
kernel32.GetCurrentProcess.restype = ctypes.c_void_p
|
||||
process = kernel32.GetCurrentProcess()
|
||||
|
||||
token = ctypes.c_void_p()
|
||||
try:
|
||||
TOKEN_QUERY = 8
|
||||
assert advapi32.OpenProcessToken(ctypes.c_void_p(process), TOKEN_QUERY, ctypes.byref(token))
|
||||
|
||||
elevation_type = ctypes.c_int()
|
||||
TokenElevationType = 18
|
||||
assert advapi32.GetTokenInformation(
|
||||
token, TokenElevationType, ctypes.byref(elevation_type), ctypes.sizeof(elevation_type),
|
||||
ctypes.byref(ctypes.c_int())
|
||||
)
|
||||
finally:
|
||||
kernel32.CloseHandle(token)
|
||||
|
||||
if elevation_type.value == 2: # TokenElevationTypeFull
|
||||
logger.log(
|
||||
logging.DEPRECATION,
|
||||
"Running PyInstaller as admin is not necessary nor sensible. Run PyInstaller from a non-administrator "
|
||||
"terminal. PyInstaller 7.0 will block this."
|
||||
)
|
||||
|
||||
elif compat.is_darwin or compat.is_linux:
|
||||
# Discourage (with the intention to eventually block) people using *sudo* with PyInstaller.
|
||||
# Again there are 4 cases, block only case 4.
|
||||
# 1. Non-root: os.getuid() != 0
|
||||
# 2. Logged in as root (usually a VM): os.getlogin() == "root", os.getuid() == 0
|
||||
# 3. No named users (e.g. most Docker containers): os.getlogin() fails
|
||||
# 4. Regular user using escalation: os.getlogin() != "root", os.getuid() == 0
|
||||
try:
|
||||
user = os.getlogin()
|
||||
except OSError:
|
||||
user = ""
|
||||
if os.getuid() == 0 and user and user != "root":
|
||||
logger.log(
|
||||
logging.DEPRECATION,
|
||||
"Running PyInstaller as root is not necessary nor sensible. Do not use PyInstaller with sudo. "
|
||||
"PyInstaller 7.0 will block this."
|
||||
)
|
||||
|
||||
if compat.is_win:
|
||||
# Do not let people run PyInstaller from admin cmd's default working directory (C:\Windows\system32)
|
||||
cwd = pathlib.Path.cwd()
|
||||
|
||||
try:
|
||||
win_dir = compat.win32api.GetWindowsDirectory()
|
||||
except Exception:
|
||||
win_dir = None
|
||||
win_dir = None if win_dir is None else pathlib.Path(win_dir).resolve()
|
||||
|
||||
inside_win_dir = cwd == win_dir or win_dir in cwd.parents
|
||||
|
||||
# The only exception to the above is if user's home directory is also located under %WINDIR%, which happens
|
||||
# when PyInstaller is ran under SYSTEM user.
|
||||
if inside_win_dir:
|
||||
home_dir = pathlib.Path.home().resolve()
|
||||
if cwd == home_dir or home_dir in cwd.parents:
|
||||
inside_win_dir = False
|
||||
|
||||
if inside_win_dir:
|
||||
raise SystemExit(
|
||||
f"ERROR: Do not run pyinstaller from {cwd}. cd to where your code is and run pyinstaller from there. "
|
||||
"Hint: You can open a terminal where your code is by going to the parent folder in Windows file "
|
||||
"explorer and typing cmd into the address bar."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
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.
@@ -0,0 +1,45 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
msg = """
|
||||
=============================================================
|
||||
A RecursionError (maximum recursion depth exceeded) occurred.
|
||||
For working around please follow these instructions
|
||||
=============================================================
|
||||
|
||||
1. In your program's .spec file add this line near the top::
|
||||
|
||||
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)
|
||||
|
||||
2. Build your program by running PyInstaller with the .spec file as
|
||||
argument::
|
||||
|
||||
pyinstaller myprog.spec
|
||||
|
||||
3. If this fails, you most probably hit an endless recursion in
|
||||
PyInstaller. Please try to track this down as far as possible,
|
||||
create a minimal example so we can reproduce and open an issue at
|
||||
https://github.com/pyinstaller/pyinstaller/issues following the
|
||||
instructions in the issue template. Many thanks.
|
||||
|
||||
Explanation: Python's stack-limit is a safety-belt against endless recursion,
|
||||
eating up memory. PyInstaller imports modules recursively. If the structure
|
||||
how modules are imported within your program is awkward, this leads to the
|
||||
nesting being too deep and hitting Python's stack-limit.
|
||||
|
||||
With the default recursion limit (1000), the recursion error occurs at about
|
||||
115 nested imported, with limit 2000 at about 240, with limit 5000 at about
|
||||
660.
|
||||
"""
|
||||
|
||||
|
||||
def raise_with_msg():
|
||||
raise SystemExit(msg)
|
||||
@@ -0,0 +1,98 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Code to be shared by PyInstaller and the bootloader/wscript file.
|
||||
|
||||
This code must not assume that either PyInstaller or any of its dependencies installed. I.e., the only imports allowed
|
||||
in here are standard library ones. Within reason, it is preferable that this file should still run under Python 2.7 as
|
||||
many compiler docker images still have only Python 2 installed.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def _pyi_machine(machine, system):
|
||||
# type: (str, str) -> str
|
||||
"""
|
||||
Choose an intentionally simplified architecture identifier to be used in the bootloader's directory name.
|
||||
|
||||
Args:
|
||||
machine:
|
||||
The output of ``platform.machine()`` or any known architecture alias or shorthand that may be used by a
|
||||
C compiler.
|
||||
system:
|
||||
The output of ``platform.system()`` on the target machine.
|
||||
Returns:
|
||||
Either a string tag or, on platforms that don't need an architecture tag, ``None``.
|
||||
|
||||
Ideally, we would just use ``platform.machine()`` directly, but that makes cross-compiling the bootloader almost
|
||||
impossible, because you need to know at compile time exactly what ``platform.machine()`` will be at run time, based
|
||||
only on the machine name alias or shorthand reported by the C compiler at the build time. Rather, use a loose
|
||||
differentiation, and trust that anyone mixing armv6l with armv6h knows what they are doing.
|
||||
"""
|
||||
# See the corresponding tests in tests/unit/test_compat.py for examples.
|
||||
|
||||
if system == "Windows":
|
||||
if machine.lower().startswith("arm"):
|
||||
return "arm"
|
||||
else:
|
||||
return "intel"
|
||||
|
||||
if system == "SunOS":
|
||||
if machine.lower() in ("x86_64", "x86", "i86pc"):
|
||||
return "intel"
|
||||
else:
|
||||
return "sparc"
|
||||
|
||||
# Fold Android back into Linux. Currently, Termux environment is the only way to run PyInstaller on Android.
|
||||
# Starting with python 3.13, `platform.system()` reports 'Android' (see PEP-738); earlier versions reported 'Linux'.
|
||||
# The compiler-based target platform identification in `waf`, however, identifies target platform as Linux on all
|
||||
# python versions.
|
||||
if system == "Android":
|
||||
system = "Linux"
|
||||
|
||||
if system != "Linux":
|
||||
# No architecture specifier for anything par Linux.
|
||||
# - macOS is on two 64 bit architectures, but they are merged into one "universal2" bootloader.
|
||||
# - BSD supports a wide range of architectures, but according to PyPI's download statistics, every one of our
|
||||
# BSD users are on x86_64. This may change in the distant future.
|
||||
return
|
||||
|
||||
if machine.startswith(("arm", "aarch")):
|
||||
# ARM has a huge number of similar and aliased sub-versions, such as armv5, armv6l armv8h, aarch64.
|
||||
return "arm"
|
||||
if machine in ("thumb"):
|
||||
# Reported by waf/gcc when Thumb instruction set is enabled on 32-bit ARM. The platform.machine() returns "arm"
|
||||
# regardless of the instruction set.
|
||||
return "arm"
|
||||
if machine in ("x86_64", "x64", "x86"):
|
||||
return "intel"
|
||||
if re.fullmatch("i[1-6]86", machine):
|
||||
return "intel"
|
||||
if machine.startswith(("ppc", "powerpc")):
|
||||
# PowerPC comes in 64 vs 32 bit and little vs big endian variants.
|
||||
return "ppc"
|
||||
if machine in ("mips64", "mips"):
|
||||
return "mips"
|
||||
if machine.startswith("riscv"):
|
||||
return "riscv"
|
||||
if machine.startswith(("sw_", "sunway")):
|
||||
return "sunway"
|
||||
if machine.startswith("loongarch"):
|
||||
return "loongarch"
|
||||
# Machines with no known aliases :)
|
||||
if machine in ("s390x",):
|
||||
return machine
|
||||
|
||||
# Unknown architectures are allowed by default, but will all be placed under one directory. In theory, trying to
|
||||
# have multiple unknown architectures in one copy of PyInstaller will not work, but that should be sufficiently
|
||||
# unlikely to ever happen.
|
||||
return "unknown"
|
||||
@@ -0,0 +1 @@
|
||||
__author__ = 'martin'
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class PyiBlockCipher:
|
||||
def __init__(self, key=None):
|
||||
from PyInstaller.exceptions import RemovedCipherFeatureError
|
||||
raise RemovedCipherFeatureError("Please remove cipher and block_cipher parameters from your spec file.")
|
||||
238
venv/lib/python3.12/site-packages/PyInstaller/archive/readers.py
Normal file
238
venv/lib/python3.12/site-packages/PyInstaller/archive/readers.py
Normal file
@@ -0,0 +1,238 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Python-based CArchive (PKG) reader implementation. Used only in the archive_viewer utility.
|
||||
"""
|
||||
|
||||
import os
|
||||
import struct
|
||||
|
||||
from PyInstaller.loader.pyimod01_archive import ZlibArchiveReader, ArchiveReadError
|
||||
|
||||
|
||||
class NotAnArchiveError(TypeError):
|
||||
pass
|
||||
|
||||
|
||||
# Type codes for CArchive TOC entries
|
||||
PKG_ITEM_BINARY = 'b' # binary
|
||||
PKG_ITEM_DEPENDENCY = 'd' # runtime option
|
||||
PKG_ITEM_PYZ = 'z' # zlib (pyz) - frozen Python code
|
||||
PKG_ITEM_ZIPFILE = 'Z' # zlib (pyz) - frozen Python code
|
||||
PKG_ITEM_PYPACKAGE = 'M' # Python package (__init__.py)
|
||||
PKG_ITEM_PYMODULE = 'm' # Python module
|
||||
PKG_ITEM_PYSOURCE = 's' # Python script (v3)
|
||||
PKG_ITEM_DATA = 'x' # data
|
||||
PKG_ITEM_RUNTIME_OPTION = 'o' # runtime option
|
||||
PKG_ITEM_SPLASH = 'l' # splash resources
|
||||
|
||||
|
||||
class CArchiveReader:
|
||||
"""
|
||||
Reader for PyInstaller's CArchive (PKG) archive.
|
||||
"""
|
||||
|
||||
# Cookie - holds some information for the bootloader. C struct format definition. '!' at the beginning means network
|
||||
# byte order. C struct looks like:
|
||||
#
|
||||
# typedef struct _archive_cookie
|
||||
# {
|
||||
# char magic[8];
|
||||
# uint32_t pkg_length;
|
||||
# uint32_t toc_offset;
|
||||
# uint32_t toc_length;
|
||||
# uint32_t python_version;
|
||||
# char python_libname[64];
|
||||
# } ARCHIVE_COOKIE;
|
||||
#
|
||||
_COOKIE_MAGIC_PATTERN = b'MEI\014\013\012\013\016'
|
||||
|
||||
_COOKIE_FORMAT = '!8sIIII64s'
|
||||
_COOKIE_LENGTH = struct.calcsize(_COOKIE_FORMAT)
|
||||
|
||||
# TOC entry:
|
||||
#
|
||||
# typedef struct _toc_entry
|
||||
# {
|
||||
# uint32_t entry_length;
|
||||
# uint32_t offset;
|
||||
# uint32_t length;
|
||||
# uint32_t uncompressed_length;
|
||||
# unsigned char compression_flag;
|
||||
# char typecode;
|
||||
# char name[1]; /* Variable-length name, padded to multiple of 16 */
|
||||
# } TOC_ENTRY;
|
||||
#
|
||||
_TOC_ENTRY_FORMAT = '!IIIIBc'
|
||||
_TOC_ENTRY_LENGTH = struct.calcsize(_TOC_ENTRY_FORMAT)
|
||||
|
||||
def __init__(self, filename):
|
||||
self._filename = filename
|
||||
self._start_offset = 0
|
||||
self._end_offset = 0
|
||||
self._toc_offset = 0
|
||||
self._toc_length = 0
|
||||
|
||||
self.toc = {}
|
||||
self.options = []
|
||||
|
||||
# Load TOC
|
||||
with open(self._filename, "rb") as fp:
|
||||
# Find cookie MAGIC pattern
|
||||
cookie_start_offset = self._find_magic_pattern(fp, self._COOKIE_MAGIC_PATTERN)
|
||||
if cookie_start_offset == -1:
|
||||
raise ArchiveReadError("Could not find COOKIE magic pattern!")
|
||||
|
||||
# Read the whole cookie
|
||||
fp.seek(cookie_start_offset, os.SEEK_SET)
|
||||
cookie_data = fp.read(self._COOKIE_LENGTH)
|
||||
|
||||
magic, archive_length, toc_offset, toc_length, pyvers, pylib_name = \
|
||||
struct.unpack(self._COOKIE_FORMAT, cookie_data)
|
||||
|
||||
# Compute start and end offset of the the archive
|
||||
self._end_offset = cookie_start_offset + self._COOKIE_LENGTH
|
||||
self._start_offset = self._end_offset - archive_length
|
||||
|
||||
# Verify that Python shared library name is set
|
||||
if not pylib_name:
|
||||
raise ArchiveReadError("Python shared library name not set in the archive!")
|
||||
|
||||
# Read whole toc
|
||||
fp.seek(self._start_offset + toc_offset)
|
||||
toc_data = fp.read(toc_length)
|
||||
|
||||
self.toc, self.options = self._parse_toc(toc_data)
|
||||
|
||||
@staticmethod
|
||||
def _find_magic_pattern(fp, magic_pattern):
|
||||
# Start at the end of file, and scan back-to-start
|
||||
fp.seek(0, os.SEEK_END)
|
||||
end_pos = fp.tell()
|
||||
|
||||
# Scan from back
|
||||
SEARCH_CHUNK_SIZE = 8192
|
||||
magic_offset = -1
|
||||
while end_pos >= len(magic_pattern):
|
||||
start_pos = max(end_pos - SEARCH_CHUNK_SIZE, 0)
|
||||
chunk_size = end_pos - start_pos
|
||||
# Is the remaining chunk large enough to hold the pattern?
|
||||
if chunk_size < len(magic_pattern):
|
||||
break
|
||||
# Read and scan the chunk
|
||||
fp.seek(start_pos, os.SEEK_SET)
|
||||
buf = fp.read(chunk_size)
|
||||
pos = buf.rfind(magic_pattern)
|
||||
if pos != -1:
|
||||
magic_offset = start_pos + pos
|
||||
break
|
||||
# Adjust search location for next chunk; ensure proper overlap
|
||||
end_pos = start_pos + len(magic_pattern) - 1
|
||||
|
||||
return magic_offset
|
||||
|
||||
@classmethod
|
||||
def _parse_toc(cls, data):
|
||||
options = []
|
||||
toc = {}
|
||||
cur_pos = 0
|
||||
while cur_pos < len(data):
|
||||
# Read and parse the fixed-size TOC entry header
|
||||
entry_length, entry_offset, data_length, uncompressed_length, compression_flag, typecode = \
|
||||
struct.unpack(cls._TOC_ENTRY_FORMAT, data[cur_pos:(cur_pos + cls._TOC_ENTRY_LENGTH)])
|
||||
cur_pos += cls._TOC_ENTRY_LENGTH
|
||||
# Read variable-length name
|
||||
name_length = entry_length - cls._TOC_ENTRY_LENGTH
|
||||
name, *_ = struct.unpack(f'{name_length}s', data[cur_pos:(cur_pos + name_length)])
|
||||
cur_pos += name_length
|
||||
# Name string may contain up to 15 bytes of padding
|
||||
name = name.rstrip(b'\0').decode('utf-8')
|
||||
|
||||
typecode = typecode.decode('ascii')
|
||||
|
||||
# The TOC should not contain duplicates, except for OPTION entries. Therefore, keep those
|
||||
# in a separate list. With options, the rest of the entries do not make sense, anyway.
|
||||
if typecode == 'o':
|
||||
options.append(name)
|
||||
else:
|
||||
toc[name] = (entry_offset, data_length, uncompressed_length, compression_flag, typecode)
|
||||
|
||||
return toc, options
|
||||
|
||||
def extract(self, name):
|
||||
"""
|
||||
Extract data for the given entry name.
|
||||
"""
|
||||
|
||||
entry = self.toc.get(name)
|
||||
if entry is None:
|
||||
raise KeyError(f"No entry named {name!r} found in the archive!")
|
||||
|
||||
entry_offset, data_length, uncompressed_length, compression_flag, typecode = entry
|
||||
with open(self._filename, "rb") as fp:
|
||||
fp.seek(self._start_offset + entry_offset, os.SEEK_SET)
|
||||
data = fp.read(data_length)
|
||||
|
||||
if compression_flag:
|
||||
import zlib
|
||||
data = zlib.decompress(data)
|
||||
|
||||
return data
|
||||
|
||||
def raw_pkg_data(self):
|
||||
"""
|
||||
Extract complete PKG/CArchive archive from the parent file (executable).
|
||||
"""
|
||||
total_length = self._end_offset - self._start_offset
|
||||
with open(self._filename, "rb") as fp:
|
||||
fp.seek(self._start_offset, os.SEEK_SET)
|
||||
return fp.read(total_length)
|
||||
|
||||
def open_embedded_archive(self, name):
|
||||
"""
|
||||
Open new archive reader for the embedded archive.
|
||||
"""
|
||||
|
||||
entry = self.toc.get(name)
|
||||
if entry is None:
|
||||
raise KeyError(f"No entry named {name!r} found in the archive!")
|
||||
|
||||
entry_offset, data_length, uncompressed_length, compression_flag, typecode = entry
|
||||
|
||||
if typecode == PKG_ITEM_PYZ:
|
||||
# Open as embedded archive, without extraction.
|
||||
return ZlibArchiveReader(self._filename, self._start_offset + entry_offset)
|
||||
elif typecode == PKG_ITEM_ZIPFILE:
|
||||
raise NotAnArchiveError("Zipfile archives not supported yet!")
|
||||
else:
|
||||
raise NotAnArchiveError(f"Entry {name!r} is not a supported embedded archive!")
|
||||
|
||||
|
||||
def pkg_archive_contents(filename, recursive=True):
|
||||
"""
|
||||
List the contents of the PKG / CArchive. If `recursive` flag is set (the default), the contents of the embedded PYZ
|
||||
archive is included as well.
|
||||
|
||||
Used by the tests.
|
||||
"""
|
||||
|
||||
contents = []
|
||||
|
||||
pkg_archive = CArchiveReader(filename)
|
||||
for name, toc_entry in pkg_archive.toc.items():
|
||||
*_, typecode = toc_entry
|
||||
contents.append(name)
|
||||
if typecode == PKG_ITEM_PYZ and recursive:
|
||||
pyz_archive = pkg_archive.open_embedded_archive(name)
|
||||
for name in pyz_archive.toc.keys():
|
||||
contents.append(name)
|
||||
|
||||
return contents
|
||||
469
venv/lib/python3.12/site-packages/PyInstaller/archive/writers.py
Normal file
469
venv/lib/python3.12/site-packages/PyInstaller/archive/writers.py
Normal file
@@ -0,0 +1,469 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Utilities to create data structures for embedding Python modules and additional files into the executable.
|
||||
"""
|
||||
|
||||
import marshal
|
||||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
import zlib
|
||||
|
||||
from PyInstaller.building.utils import get_code_object, replace_filename_in_code_object
|
||||
from PyInstaller.compat import BYTECODE_MAGIC, is_win, strict_collect_mode
|
||||
from PyInstaller.loader.pyimod01_archive import PYZ_ITEM_MODULE, PYZ_ITEM_NSPKG, PYZ_ITEM_PKG
|
||||
|
||||
|
||||
class ZlibArchiveWriter:
|
||||
"""
|
||||
Writer for PyInstaller's PYZ (ZlibArchive) archive. The archive is used to store collected byte-compiled Python
|
||||
modules, as individually-compressed entries.
|
||||
"""
|
||||
_PYZ_MAGIC_PATTERN = b'PYZ\0'
|
||||
_HEADER_LENGTH = 12 + 5
|
||||
_COMPRESSION_LEVEL = 6 # zlib compression level
|
||||
|
||||
def __init__(self, filename, entries, code_dict=None):
|
||||
"""
|
||||
filename
|
||||
Target filename of the archive.
|
||||
entries
|
||||
An iterable containing entries in the form of tuples: (name, src_path, typecode), where `name` is the name
|
||||
under which the resource is stored (e.g., python module name, without suffix), `src_path` is name of the
|
||||
file from which the resource is read, and `typecode` is the Analysis-level TOC typecode (`PYMODULE`).
|
||||
code_dict
|
||||
Optional code dictionary containing code objects for analyzed/collected python modules.
|
||||
"""
|
||||
code_dict = code_dict or {}
|
||||
|
||||
with open(filename, "wb") as fp:
|
||||
# Reserve space for the header.
|
||||
fp.write(b'\0' * self._HEADER_LENGTH)
|
||||
|
||||
# Write entries' data and collect TOC entries
|
||||
toc = []
|
||||
for entry in entries:
|
||||
toc_entry = self._write_entry(fp, entry, code_dict)
|
||||
toc.append(toc_entry)
|
||||
|
||||
# Write TOC
|
||||
toc_offset = fp.tell()
|
||||
toc_data = marshal.dumps(toc)
|
||||
fp.write(toc_data)
|
||||
|
||||
# Write header:
|
||||
# - PYZ magic pattern (4 bytes)
|
||||
# - python bytecode magic pattern (4 bytes)
|
||||
# - TOC offset (32-bit int, 4 bytes)
|
||||
# - 4 unused bytes
|
||||
fp.seek(0, os.SEEK_SET)
|
||||
|
||||
fp.write(self._PYZ_MAGIC_PATTERN)
|
||||
fp.write(BYTECODE_MAGIC)
|
||||
fp.write(struct.pack('!i', toc_offset))
|
||||
|
||||
@classmethod
|
||||
def _write_entry(cls, fp, entry, code_dict):
|
||||
name, src_path, typecode = entry
|
||||
assert typecode in {'PYMODULE', 'PYMODULE-1', 'PYMODULE-2'}
|
||||
|
||||
if src_path in {'-', None}:
|
||||
# PEP-420 namespace package; these do not have code objects, but we still need an entry in PYZ to inform our
|
||||
# run-time module finder/loader of the package's existence. So create a TOC entry for 0-byte data blob,
|
||||
# and write no data.
|
||||
return (name, (PYZ_ITEM_NSPKG, fp.tell(), 0))
|
||||
|
||||
code_object = code_dict[name]
|
||||
|
||||
src_basename, _ = os.path.splitext(os.path.basename(src_path))
|
||||
if src_basename == '__init__':
|
||||
typecode = PYZ_ITEM_PKG
|
||||
co_filename = os.path.join(*name.split('.'), '__init__.py')
|
||||
else:
|
||||
typecode = PYZ_ITEM_MODULE
|
||||
co_filename = os.path.join(*name.split('.')) + '.py'
|
||||
|
||||
# Replace co_filename on code object with anonymized version without absolute path to the module.
|
||||
code_object = replace_filename_in_code_object(code_object, co_filename)
|
||||
|
||||
# Serialize
|
||||
data = marshal.dumps(code_object)
|
||||
|
||||
# First compress, then encrypt.
|
||||
obj = zlib.compress(data, cls._COMPRESSION_LEVEL)
|
||||
|
||||
# Create TOC entry
|
||||
toc_entry = (name, (typecode, fp.tell(), len(obj)))
|
||||
|
||||
# Write data blob
|
||||
fp.write(obj)
|
||||
|
||||
return toc_entry
|
||||
|
||||
|
||||
class CArchiveWriter:
|
||||
"""
|
||||
Writer for PyInstaller's CArchive (PKG) archive.
|
||||
|
||||
This archive contains all files that are bundled within an executable; a PYZ (ZlibArchive), DLLs, Python C
|
||||
extensions, and other data files that are bundled in onefile mode.
|
||||
|
||||
The archive can be read from either C (bootloader code at application's run-time) or Python (for debug purposes).
|
||||
"""
|
||||
_COOKIE_MAGIC_PATTERN = b'MEI\014\013\012\013\016'
|
||||
|
||||
# For cookie and TOC entry structure, see `PyInstaller.archive.readers.CArchiveReader`.
|
||||
_COOKIE_FORMAT = '!8sIIII64s'
|
||||
_COOKIE_LENGTH = struct.calcsize(_COOKIE_FORMAT)
|
||||
|
||||
_TOC_ENTRY_FORMAT = '!IIIIBc'
|
||||
_TOC_ENTRY_LENGTH = struct.calcsize(_TOC_ENTRY_FORMAT)
|
||||
|
||||
_COMPRESSION_LEVEL = 9 # zlib compression level
|
||||
|
||||
def __init__(self, filename, entries, pylib_name):
|
||||
"""
|
||||
filename
|
||||
Target filename of the archive.
|
||||
entries
|
||||
An iterable containing entries in the form of tuples: (dest_name, src_name, compress, typecode), where
|
||||
`dest_name` is the name under which the resource is stored in the archive (and name under which it is
|
||||
extracted at runtime), `src_name` is name of the file from which the resouce is read, `compress` is a
|
||||
boolean compression flag, and `typecode` is the Analysis-level TOC typecode.
|
||||
pylib_name
|
||||
Name of the python shared library.
|
||||
"""
|
||||
self._collected_names = set() # Track collected names for strict package mode.
|
||||
|
||||
with open(filename, "wb") as fp:
|
||||
# Write entries' data and collect TOC entries
|
||||
toc = []
|
||||
for entry in entries:
|
||||
toc_entry = self._write_entry(fp, entry)
|
||||
toc.append(toc_entry)
|
||||
|
||||
# Write TOC
|
||||
toc_offset = fp.tell()
|
||||
toc_data = self._serialize_toc(toc)
|
||||
toc_length = len(toc_data)
|
||||
|
||||
fp.write(toc_data)
|
||||
|
||||
# Write cookie
|
||||
archive_length = toc_offset + toc_length + self._COOKIE_LENGTH
|
||||
pyvers = sys.version_info[0] * 100 + sys.version_info[1]
|
||||
cookie_data = struct.pack(
|
||||
self._COOKIE_FORMAT,
|
||||
self._COOKIE_MAGIC_PATTERN,
|
||||
archive_length,
|
||||
toc_offset,
|
||||
toc_length,
|
||||
pyvers,
|
||||
pylib_name.encode('ascii'),
|
||||
)
|
||||
|
||||
fp.write(cookie_data)
|
||||
|
||||
def _write_entry(self, fp, entry):
|
||||
dest_name, src_name, compress, typecode = entry
|
||||
|
||||
# Write OPTION entries as-is, without normalizing them. This also exempts them from duplication check,
|
||||
# allowing them to be specified multiple times.
|
||||
if typecode == 'o':
|
||||
return self._write_blob(fp, b"", dest_name, typecode)
|
||||
|
||||
# Ensure forward slashes in paths are on Windows converted to back slashes '\\', as on Windows the bootloader
|
||||
# works only with back slashes.
|
||||
dest_name = os.path.normpath(dest_name)
|
||||
if is_win and os.path.sep == '/':
|
||||
# When building under MSYS, the above path normalization uses Unix-style separators, so replace them
|
||||
# manually.
|
||||
dest_name = dest_name.replace(os.path.sep, '\\')
|
||||
|
||||
# For symbolic link entries, also ensure that the symlink target path (stored in src_name) is using
|
||||
# Windows-style back slash separators.
|
||||
if typecode == 'n':
|
||||
src_name = src_name.replace(os.path.sep, '\\')
|
||||
|
||||
# Strict pack/collect mode: keep track of the destination names, and raise an error if we try to add a duplicate
|
||||
# (a file with same destination name, subject to OS case normalization rules).
|
||||
if strict_collect_mode:
|
||||
normalized_dest = None
|
||||
if typecode in {'s', 's1', 's2', 'm', 'M'}:
|
||||
# Exempt python source scripts and modules from the check.
|
||||
pass
|
||||
else:
|
||||
# Everything else; normalize the case
|
||||
normalized_dest = os.path.normcase(dest_name)
|
||||
# Check for existing entry, if applicable
|
||||
if normalized_dest:
|
||||
if normalized_dest in self._collected_names:
|
||||
raise ValueError(
|
||||
f"Attempting to collect a duplicated file into CArchive: {normalized_dest} (type: {typecode})"
|
||||
)
|
||||
self._collected_names.add(normalized_dest)
|
||||
|
||||
if typecode == 'd':
|
||||
# Dependency; merge src_name (= reference path prefix) and dest_name (= name) into single-string format that
|
||||
# is parsed by bootloader.
|
||||
return self._write_blob(fp, b"", f"{src_name}:{dest_name}", typecode)
|
||||
elif typecode in {'s', 's1', 's2'}:
|
||||
# If it is a source code file, compile it to a code object and marshal the object, so it can be unmarshalled
|
||||
# by the bootloader. For that, we need to know target optimization level, which is stored in typecode.
|
||||
optim_level = {'s': 0, 's1': 1, 's2': 2}[typecode]
|
||||
code = get_code_object(dest_name, src_name, optimize=optim_level)
|
||||
# Construct new `co_filename` by taking destination name, and replace its suffix with the one from the code
|
||||
# object's co_filename; this should cover all of the following cases:
|
||||
# - run-time hook script: the source name has a suffix (that is also present in `co_filename` produced by
|
||||
# `get_code_object`), destination name has no suffix.
|
||||
# - entry-point script with a suffix: both source name and destination name have the same suffix (and the
|
||||
# same suffix is also in `co_filename` produced by `get_code_object`)
|
||||
# - entry-point script without a suffix: neither source name nor destination name have a suffix, but
|
||||
# `get_code_object` adds a .py suffix to `co_filename` to mitigate potential issues with POSIX
|
||||
# executables and `traceback` module; we want to preserve this behavior.
|
||||
co_filename = os.path.splitext(dest_name)[0] + os.path.splitext(code.co_filename)[1]
|
||||
code = replace_filename_in_code_object(code, co_filename)
|
||||
return self._write_blob(fp, marshal.dumps(code), dest_name, 's', compress=compress)
|
||||
elif typecode in ('m', 'M'):
|
||||
# Read the PYC file. We do not perform compilation here (in contrast to script files in the above branch),
|
||||
# so typecode does not contain optimization level information.
|
||||
with open(src_name, "rb") as in_fp:
|
||||
data = in_fp.read()
|
||||
assert data[:4] == BYTECODE_MAGIC
|
||||
# Skip the PYC header, load the code object.
|
||||
code = marshal.loads(data[16:])
|
||||
co_filename = dest_name + '.py' # Use dest name with added .py suffix.
|
||||
code = replace_filename_in_code_object(code, co_filename)
|
||||
# These module entries are loaded and executed within the bootloader, which requires only the code
|
||||
# object, without the PYC header.
|
||||
return self._write_blob(fp, marshal.dumps(code), dest_name, typecode, compress=compress)
|
||||
elif typecode == 'n':
|
||||
# Symbolic link; store target name (as NULL-terminated string)
|
||||
data = src_name.encode('utf-8') + b'\x00'
|
||||
return self._write_blob(fp, data, dest_name, typecode, compress=compress)
|
||||
else:
|
||||
return self._write_file(fp, src_name, dest_name, typecode, compress=compress)
|
||||
|
||||
def _write_blob(self, out_fp, blob: bytes, dest_name, typecode, compress=False):
|
||||
"""
|
||||
Write the binary contents (**blob**) of a small file to the archive and return the corresponding CArchive TOC
|
||||
entry.
|
||||
"""
|
||||
data_offset = out_fp.tell()
|
||||
data_length = len(blob)
|
||||
if compress:
|
||||
blob = zlib.compress(blob, level=self._COMPRESSION_LEVEL)
|
||||
out_fp.write(blob)
|
||||
|
||||
return (data_offset, len(blob), data_length, int(compress), typecode, dest_name)
|
||||
|
||||
def _write_file(self, out_fp, src_name, dest_name, typecode, compress=False):
|
||||
"""
|
||||
Stream copy a large file into the archive and return the corresponding CArchive TOC entry.
|
||||
"""
|
||||
data_offset = out_fp.tell()
|
||||
data_length = os.stat(src_name).st_size
|
||||
with open(src_name, 'rb') as in_fp:
|
||||
if compress:
|
||||
tmp_buffer = bytearray(16 * 1024)
|
||||
compressor = zlib.compressobj(self._COMPRESSION_LEVEL)
|
||||
while True:
|
||||
num_read = in_fp.readinto(tmp_buffer)
|
||||
if not num_read:
|
||||
break
|
||||
out_fp.write(compressor.compress(tmp_buffer[:num_read]))
|
||||
out_fp.write(compressor.flush())
|
||||
else:
|
||||
shutil.copyfileobj(in_fp, out_fp)
|
||||
|
||||
return (data_offset, out_fp.tell() - data_offset, data_length, int(compress), typecode, dest_name)
|
||||
|
||||
@classmethod
|
||||
def _serialize_toc(cls, toc):
|
||||
serialized_toc = []
|
||||
for toc_entry in toc:
|
||||
data_offset, compressed_length, data_length, compress, typecode, name = toc_entry
|
||||
|
||||
# Encode names as UTF-8. This should be safe as standard python modules only contain ASCII-characters (and
|
||||
# standard shared libraries should have the same), and thus the C-code still can handle this correctly.
|
||||
name = name.encode('utf-8')
|
||||
name_length = len(name) + 1 # Add 1 for string-terminating zero byte.
|
||||
|
||||
# Ensure TOC entries are aligned on 16-byte boundary, so they can be read by bootloader (C code) on
|
||||
# platforms with strict data alignment requirements (for example linux on `armhf`/`armv7`, such as 32-bit
|
||||
# Debian Buster on Raspberry Pi).
|
||||
entry_length = cls._TOC_ENTRY_LENGTH + name_length
|
||||
if entry_length % 16 != 0:
|
||||
padding_length = 16 - (entry_length % 16)
|
||||
name_length += padding_length
|
||||
|
||||
# Serialize
|
||||
serialized_entry = struct.pack(
|
||||
cls._TOC_ENTRY_FORMAT + f"{name_length}s", # "Ns" format automatically pads the string with zero bytes.
|
||||
cls._TOC_ENTRY_LENGTH + name_length,
|
||||
data_offset,
|
||||
compressed_length,
|
||||
data_length,
|
||||
compress,
|
||||
typecode.encode('ascii'),
|
||||
name,
|
||||
)
|
||||
serialized_toc.append(serialized_entry)
|
||||
|
||||
return b''.join(serialized_toc)
|
||||
|
||||
|
||||
class SplashWriter:
|
||||
"""
|
||||
Writer for the splash screen resources archive.
|
||||
|
||||
The resulting archive is added as an entry into the CArchive with the typecode PKG_ITEM_SPLASH.
|
||||
"""
|
||||
# This struct describes the splash resources as it will be in an buffer inside the bootloader. All necessary parts
|
||||
# are bundled, the *_len and *_offset fields describe the data beyond this header definition.
|
||||
# Whereas script and image fields are binary data, the requirements fields describe an array of strings. Each string
|
||||
# is null-terminated in order to easily iterate over this list from within C.
|
||||
#
|
||||
# typedef struct _splash_data_header
|
||||
# {
|
||||
# char tcl_shared_library_name[32];
|
||||
# char tk_shared_library_name[32];
|
||||
# char tcl_module_directory_name[16];
|
||||
# char tk_module_directory_name[16];
|
||||
#
|
||||
# uint32_t script_len;
|
||||
# uint32_t script_offset;
|
||||
#
|
||||
# uint32_t image_len;
|
||||
# uint32_t image_offset;
|
||||
#
|
||||
# uint32_t requirements_len;
|
||||
# uint32_t requirements_offset;
|
||||
#
|
||||
# uint32_t centering_mode;
|
||||
# } SPLASH_DATA_HEADER;
|
||||
#
|
||||
_HEADER_FORMAT = '!32s 32s 16s 16s II II II I'
|
||||
_HEADER_LENGTH = struct.calcsize(_HEADER_FORMAT)
|
||||
|
||||
# Centering mode values - keep in sync with values defined in `bootloader/src/pyi_splash.h`!
|
||||
_SPLASH_CENTER_DEFAULT = 0
|
||||
_SPLASH_CENTER_VIRTUAL_SCREEN = 1
|
||||
_SPLASH_CENTER_PRIMARY_SCREEN = 2
|
||||
_SPLASH_CENTER_ACTIVE_SCREEN = 3
|
||||
|
||||
# The created archive is compressed by the CArchive, so no need to compress the data here.
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
filename,
|
||||
requirements_list,
|
||||
tcl_shared_library_name,
|
||||
tk_shared_library_name,
|
||||
tcl_module_directory_name,
|
||||
tk_module_directory_name,
|
||||
image,
|
||||
script,
|
||||
center_mode,
|
||||
):
|
||||
"""
|
||||
Writer for splash screen resources that are bundled into the CArchive as a single archive/entry.
|
||||
|
||||
:param filename: The filename of the archive to create
|
||||
:param requirements_list: List of filenames for the requirements array
|
||||
:param str tcl_shared_library_name: Basename of the Tcl shared library
|
||||
:param str tk_shared_library_name: Basename of the Tk shared library
|
||||
:param str tcl_module_directory_name: Basename of the Tcl module directory (e.g., tcl/)
|
||||
:param str tk_module_directory_name: Basename of the Tk module directory (e.g., tk/)
|
||||
:param Union[str, bytes] image: Image like object
|
||||
:param str script: The tcl/tk script to execute to create the screen.
|
||||
:param str center_mode: Splash screen centering mode (integer value that matches enum used by bootloader)
|
||||
"""
|
||||
|
||||
# Ensure forward slashes in dependency names are on Windows converted to back slashes '\\', as on Windows the
|
||||
# bootloader works only with back slashes.
|
||||
def _normalize_filename(filename):
|
||||
filename = os.path.normpath(filename)
|
||||
if is_win and os.path.sep == '/':
|
||||
# When building under MSYS, the above path normalization uses Unix-style separators, so replace them
|
||||
# manually.
|
||||
filename = filename.replace(os.path.sep, '\\')
|
||||
return filename
|
||||
|
||||
requirements_list = [_normalize_filename(name) for name in requirements_list]
|
||||
|
||||
with open(filename, "wb") as fp:
|
||||
# Reserve space for the header.
|
||||
fp.write(b'\0' * self._HEADER_LENGTH)
|
||||
|
||||
# Serialize the requirements list. This list (more an array) contains the names of all files the bootloader
|
||||
# needs to extract before the splash screen can be started. The implementation terminates every name with a
|
||||
# null-byte, that keeps the list short memory wise and makes it iterable from C.
|
||||
requirements_len = 0
|
||||
requirements_offset = fp.tell()
|
||||
for name in requirements_list:
|
||||
name = name.encode('utf-8') + b'\0'
|
||||
fp.write(name)
|
||||
requirements_len += len(name)
|
||||
|
||||
# Write splash script
|
||||
script_offset = fp.tell()
|
||||
script_len = len(script)
|
||||
fp.write(script.encode("utf-8"))
|
||||
|
||||
# Write splash image. If image is a bytes buffer, it is written directly into the archive. Otherwise, it
|
||||
# is assumed to be a path and the file is copied into the archive.
|
||||
image_offset = fp.tell()
|
||||
if isinstance(image, bytes):
|
||||
# Image was converted by PIL/Pillow and is already in buffer
|
||||
image_len = len(image)
|
||||
fp.write(image)
|
||||
else:
|
||||
# Read image into buffer
|
||||
with open(image, 'rb') as image_fp:
|
||||
image_data = image_fp.read()
|
||||
image_len = len(image_data)
|
||||
fp.write(image_data)
|
||||
del image_data
|
||||
|
||||
# The following strings are written to 16-character fields with zero-padding, which means that we need to
|
||||
# ensure that their length is strictly below 16 characters (if it were exactly 16, the field would have no
|
||||
# terminating NULL character!).
|
||||
def _encode_str(value, field_name, limit):
|
||||
enc_value = value.encode("utf-8")
|
||||
if len(enc_value) >= limit:
|
||||
raise ValueError(
|
||||
f"Length of the encoded field {field_name!r} ({len(enc_value)}) is greater or equal to the "
|
||||
f"limit of {limit} characters!"
|
||||
)
|
||||
|
||||
return enc_value
|
||||
|
||||
# Write header
|
||||
header_data = struct.pack(
|
||||
self._HEADER_FORMAT,
|
||||
_encode_str(tcl_shared_library_name, 'tcl_shared_library_name', 32),
|
||||
_encode_str(tk_shared_library_name, 'tk_shared_library_name', 32),
|
||||
_encode_str(tcl_module_directory_name, 'tcl_module_directory_name', 16),
|
||||
_encode_str(tk_module_directory_name, 'tk_module_directory_name', 16),
|
||||
script_len,
|
||||
script_offset,
|
||||
image_len,
|
||||
image_offset,
|
||||
requirements_len,
|
||||
requirements_offset,
|
||||
center_mode,
|
||||
)
|
||||
|
||||
fp.seek(0, os.SEEK_SET)
|
||||
fp.write(header_data)
|
||||
BIN
venv/lib/python3.12/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run
Executable file
BIN
venv/lib/python3.12/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run
Executable file
Binary file not shown.
BIN
venv/lib/python3.12/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run_d
Executable file
BIN
venv/lib/python3.12/site-packages/PyInstaller/bootloader/Linux-64bit-intel/run_d
Executable file
Binary file not shown.
@@ -0,0 +1 @@
|
||||
#
|
||||
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.
1337
venv/lib/python3.12/site-packages/PyInstaller/building/api.py
Normal file
1337
venv/lib/python3.12/site-packages/PyInstaller/building/api.py
Normal file
File diff suppressed because it is too large
Load Diff
1275
venv/lib/python3.12/site-packages/PyInstaller/building/build_main.py
Normal file
1275
venv/lib/python3.12/site-packages/PyInstaller/building/build_main.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,459 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import warnings
|
||||
|
||||
from PyInstaller import log as logging
|
||||
from PyInstaller.building.utils import _check_guts_eq
|
||||
from PyInstaller.utils import misc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def unique_name(entry):
|
||||
"""
|
||||
Return the filename used to enforce uniqueness for the given TOC entry.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
entry : tuple
|
||||
|
||||
Returns
|
||||
-------
|
||||
unique_name: str
|
||||
"""
|
||||
name, path, typecode = entry
|
||||
if typecode in ('BINARY', 'DATA', 'EXTENSION', 'DEPENDENCY'):
|
||||
name = os.path.normcase(name)
|
||||
|
||||
return name
|
||||
|
||||
|
||||
# This class is deprecated and has been replaced by plain lists with explicit normalization (de-duplication) via
|
||||
# `normalize_toc` and `normalize_pyz_toc` helper functions.
|
||||
class TOC(list):
|
||||
"""
|
||||
TOC (Table of Contents) class is a list of tuples of the form (name, path, typecode).
|
||||
|
||||
typecode name path description
|
||||
--------------------------------------------------------------------------------------
|
||||
EXTENSION Python internal name. Full path name in build. Extension module.
|
||||
PYSOURCE Python internal name. Full path name in build. Script.
|
||||
PYMODULE Python internal name. Full path name in build. Pure Python module (including __init__ modules).
|
||||
PYZ Runtime name. Full path name in build. A .pyz archive (ZlibArchive data structure).
|
||||
PKG Runtime name. Full path name in build. A .pkg archive (Carchive data structure).
|
||||
BINARY Runtime name. Full path name in build. Shared library.
|
||||
DATA Runtime name. Full path name in build. Arbitrary files.
|
||||
OPTION The option. Unused. Python runtime option (frozen into executable).
|
||||
|
||||
A TOC contains various types of files. A TOC contains no duplicates and preserves order.
|
||||
PyInstaller uses TOC data type to collect necessary files bundle them into an executable.
|
||||
"""
|
||||
def __init__(self, initlist=None):
|
||||
super().__init__()
|
||||
|
||||
# Deprecation warning
|
||||
warnings.warn(
|
||||
"TOC class is deprecated. Use a plain list of 3-element tuples instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
self.filenames = set()
|
||||
if initlist:
|
||||
for entry in initlist:
|
||||
self.append(entry)
|
||||
|
||||
def append(self, entry):
|
||||
if not isinstance(entry, tuple):
|
||||
logger.info("TOC found a %s, not a tuple", entry)
|
||||
raise TypeError("Expected tuple, not %s." % type(entry).__name__)
|
||||
|
||||
unique = unique_name(entry)
|
||||
|
||||
if unique not in self.filenames:
|
||||
self.filenames.add(unique)
|
||||
super().append(entry)
|
||||
|
||||
def insert(self, pos, entry):
|
||||
if not isinstance(entry, tuple):
|
||||
logger.info("TOC found a %s, not a tuple", entry)
|
||||
raise TypeError("Expected tuple, not %s." % type(entry).__name__)
|
||||
unique = unique_name(entry)
|
||||
|
||||
if unique not in self.filenames:
|
||||
self.filenames.add(unique)
|
||||
super().insert(pos, entry)
|
||||
|
||||
def __add__(self, other):
|
||||
result = TOC(self)
|
||||
result.extend(other)
|
||||
return result
|
||||
|
||||
def __radd__(self, other):
|
||||
result = TOC(other)
|
||||
result.extend(self)
|
||||
return result
|
||||
|
||||
def __iadd__(self, other):
|
||||
for entry in other:
|
||||
self.append(entry)
|
||||
return self
|
||||
|
||||
def extend(self, other):
|
||||
# TODO: look if this can be done more efficient with out the loop, e.g. by not using a list as base at all.
|
||||
for entry in other:
|
||||
self.append(entry)
|
||||
|
||||
def __sub__(self, other):
|
||||
# Construct new TOC with entries not contained in the other TOC
|
||||
other = TOC(other)
|
||||
return TOC([entry for entry in self if unique_name(entry) not in other.filenames])
|
||||
|
||||
def __rsub__(self, other):
|
||||
result = TOC(other)
|
||||
return result.__sub__(self)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if isinstance(key, slice):
|
||||
if key == slice(None, None, None):
|
||||
# special case: set the entire list
|
||||
self.filenames = set()
|
||||
self.clear()
|
||||
self.extend(value)
|
||||
return
|
||||
else:
|
||||
raise KeyError("TOC.__setitem__ doesn't handle slices")
|
||||
|
||||
else:
|
||||
old_value = self[key]
|
||||
old_name = unique_name(old_value)
|
||||
self.filenames.remove(old_name)
|
||||
|
||||
new_name = unique_name(value)
|
||||
if new_name not in self.filenames:
|
||||
self.filenames.add(new_name)
|
||||
super(TOC, self).__setitem__(key, value)
|
||||
|
||||
|
||||
class Target:
|
||||
invcnum = 0
|
||||
|
||||
def __init__(self):
|
||||
from PyInstaller.config import CONF
|
||||
|
||||
# Get a (per class) unique number to avoid conflicts between toc objects
|
||||
self.invcnum = self.__class__.invcnum
|
||||
self.__class__.invcnum += 1
|
||||
self.tocfilename = os.path.join(CONF['workpath'], '%s-%02d.toc' % (self.__class__.__name__, self.invcnum))
|
||||
self.tocbasename = os.path.basename(self.tocfilename)
|
||||
self.dependencies = []
|
||||
|
||||
def __postinit__(self):
|
||||
"""
|
||||
Check if the target need to be rebuild and if so, re-assemble.
|
||||
|
||||
`__postinit__` is to be called at the end of `__init__` of every subclass of Target. `__init__` is meant to
|
||||
setup the parameters and `__postinit__` is checking if rebuild is required and in case calls `assemble()`
|
||||
"""
|
||||
logger.info("checking %s", self.__class__.__name__)
|
||||
data = None
|
||||
last_build = misc.mtime(self.tocfilename)
|
||||
if last_build == 0:
|
||||
logger.info("Building %s because %s is non existent", self.__class__.__name__, self.tocbasename)
|
||||
else:
|
||||
try:
|
||||
data = misc.load_py_data_struct(self.tocfilename)
|
||||
except Exception:
|
||||
logger.info("Building because %s is bad", self.tocbasename)
|
||||
else:
|
||||
# create a dict for easier access
|
||||
data = dict(zip((g[0] for g in self._GUTS), data))
|
||||
# assemble if previous data was not found or is outdated
|
||||
if not data or self._check_guts(data, last_build):
|
||||
self.assemble()
|
||||
self._save_guts()
|
||||
|
||||
_GUTS = []
|
||||
|
||||
def _check_guts(self, data, last_build):
|
||||
"""
|
||||
Returns True if rebuild/assemble is required.
|
||||
"""
|
||||
if len(data) != len(self._GUTS):
|
||||
logger.info("Building because %s is bad", self.tocbasename)
|
||||
return True
|
||||
for attr, func in self._GUTS:
|
||||
if func is None:
|
||||
# no check for this value
|
||||
continue
|
||||
if func(attr, data[attr], getattr(self, attr), last_build):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _save_guts(self):
|
||||
"""
|
||||
Save the input parameters and the work-product of this run to maybe avoid regenerating it later.
|
||||
"""
|
||||
data = tuple(getattr(self, g[0]) for g in self._GUTS)
|
||||
misc.save_py_data_struct(self.tocfilename, data)
|
||||
|
||||
|
||||
class Tree(Target, list):
|
||||
"""
|
||||
This class is a way of creating a TOC (Table of Contents) list that describes some or all of the files within a
|
||||
directory.
|
||||
"""
|
||||
def __init__(self, root=None, prefix=None, excludes=None, typecode='DATA'):
|
||||
"""
|
||||
root
|
||||
The root of the tree (on the build system).
|
||||
prefix
|
||||
Optional prefix to the names of the target system.
|
||||
excludes
|
||||
A list of names to exclude. Two forms are allowed:
|
||||
|
||||
name
|
||||
Files with this basename will be excluded (do not include the path).
|
||||
*.ext
|
||||
Any file with the given extension will be excluded.
|
||||
typecode
|
||||
The typecode to be used for all files found in this tree. See the TOC class for for information about
|
||||
the typcodes.
|
||||
"""
|
||||
Target.__init__(self)
|
||||
list.__init__(self)
|
||||
self.root = root
|
||||
self.prefix = prefix
|
||||
self.excludes = excludes
|
||||
self.typecode = typecode
|
||||
if excludes is None:
|
||||
self.excludes = []
|
||||
self.__postinit__()
|
||||
|
||||
_GUTS = ( # input parameters
|
||||
('root', _check_guts_eq),
|
||||
('prefix', _check_guts_eq),
|
||||
('excludes', _check_guts_eq),
|
||||
('typecode', _check_guts_eq),
|
||||
('data', None), # tested below
|
||||
# no calculated/analysed values
|
||||
)
|
||||
|
||||
def _check_guts(self, data, last_build):
|
||||
if Target._check_guts(self, data, last_build):
|
||||
return True
|
||||
# Walk the collected directories as check if they have been changed - which means files have been added or
|
||||
# removed. There is no need to check for the files, since `Tree` is only about the directory contents (which is
|
||||
# the list of files).
|
||||
stack = [data['root']]
|
||||
while stack:
|
||||
d = stack.pop()
|
||||
if misc.mtime(d) > last_build:
|
||||
logger.info("Building %s because directory %s changed", self.tocbasename, d)
|
||||
return True
|
||||
for nm in os.listdir(d):
|
||||
path = os.path.join(d, nm)
|
||||
if os.path.isdir(path):
|
||||
stack.append(path)
|
||||
self[:] = data['data'] # collected files
|
||||
return False
|
||||
|
||||
def _save_guts(self):
|
||||
# Use the attribute `data` to save the list
|
||||
self.data = self
|
||||
super()._save_guts()
|
||||
del self.data
|
||||
|
||||
def assemble(self):
|
||||
logger.info("Building Tree %s", self.tocbasename)
|
||||
stack = [(self.root, self.prefix)]
|
||||
excludes = set()
|
||||
xexcludes = set()
|
||||
for name in self.excludes:
|
||||
if name.startswith('*'):
|
||||
xexcludes.add(name[1:])
|
||||
else:
|
||||
excludes.add(name)
|
||||
result = []
|
||||
while stack:
|
||||
dir, prefix = stack.pop()
|
||||
for filename in os.listdir(dir):
|
||||
if filename in excludes:
|
||||
continue
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext in xexcludes:
|
||||
continue
|
||||
fullfilename = os.path.join(dir, filename)
|
||||
if prefix:
|
||||
resfilename = os.path.join(prefix, filename)
|
||||
else:
|
||||
resfilename = filename
|
||||
if os.path.isdir(fullfilename):
|
||||
stack.append((fullfilename, resfilename))
|
||||
else:
|
||||
result.append((resfilename, fullfilename, self.typecode))
|
||||
self[:] = result
|
||||
|
||||
|
||||
def normalize_toc(toc):
|
||||
# Default priority: 0
|
||||
_TOC_TYPE_PRIORITIES = {
|
||||
# DEPENDENCY entries need to replace original entries, so they need the highest priority.
|
||||
'DEPENDENCY': 3,
|
||||
# SYMLINK entries have higher priority than other regular entries
|
||||
'SYMLINK': 2,
|
||||
# BINARY/EXTENSION entries undergo additional processing, so give them precedence over DATA and other entries.
|
||||
'BINARY': 1,
|
||||
'EXTENSION': 1,
|
||||
}
|
||||
|
||||
def _type_case_normalization_fcn(typecode):
|
||||
# Case-normalize all entries except OPTION.
|
||||
return typecode not in {
|
||||
"OPTION",
|
||||
}
|
||||
|
||||
return _normalize_toc(toc, _TOC_TYPE_PRIORITIES, _type_case_normalization_fcn)
|
||||
|
||||
|
||||
def normalize_pyz_toc(toc):
|
||||
# Default priority: 0
|
||||
_TOC_TYPE_PRIORITIES = {
|
||||
# Ensure that entries with higher optimization level take precedence.
|
||||
'PYMODULE-2': 2,
|
||||
'PYMODULE-1': 1,
|
||||
'PYMODULE': 0,
|
||||
}
|
||||
|
||||
return _normalize_toc(toc, _TOC_TYPE_PRIORITIES)
|
||||
|
||||
|
||||
def _normalize_toc(toc, toc_type_priorities, type_case_normalization_fcn=lambda typecode: False):
|
||||
options_toc = []
|
||||
tmp_toc = dict()
|
||||
for dest_name, src_name, typecode in toc:
|
||||
# Exempt OPTION entries from de-duplication processing. Some options might allow being specified multiple times.
|
||||
if typecode == 'OPTION':
|
||||
options_toc.append(((dest_name, src_name, typecode)))
|
||||
continue
|
||||
|
||||
# Always sanitize the dest_name with `os.path.normpath` to remove any local loops with parent directory path
|
||||
# components. `pathlib` does not seem to offer equivalent functionality.
|
||||
dest_name = os.path.normpath(dest_name)
|
||||
|
||||
# Normalize the destination name for uniqueness. Use `pathlib.PurePath` to ensure that keys are both
|
||||
# case-normalized (on OSes where applicable) and directory-separator normalized (just in case).
|
||||
if type_case_normalization_fcn(typecode):
|
||||
entry_key = pathlib.PurePath(dest_name)
|
||||
else:
|
||||
entry_key = dest_name
|
||||
|
||||
existing_entry = tmp_toc.get(entry_key)
|
||||
if existing_entry is None:
|
||||
# Entry does not exist - insert
|
||||
tmp_toc[entry_key] = (dest_name, src_name, typecode)
|
||||
else:
|
||||
# Entry already exists - replace if its typecode has higher priority
|
||||
_, _, existing_typecode = existing_entry
|
||||
if toc_type_priorities.get(typecode, 0) > toc_type_priorities.get(existing_typecode, 0):
|
||||
tmp_toc[entry_key] = (dest_name, src_name, typecode)
|
||||
|
||||
# Return the items as list. The order matches the original order due to python dict maintaining the insertion order.
|
||||
# The exception are OPTION entries, which are now placed at the beginning of the TOC.
|
||||
return options_toc + list(tmp_toc.values())
|
||||
|
||||
|
||||
def toc_process_symbolic_links(toc):
|
||||
"""
|
||||
Process TOC entries and replace entries whose files are symbolic links with SYMLINK entries (provided original file
|
||||
is also being collected).
|
||||
"""
|
||||
# Dictionary of all destination names, for a fast look-up.
|
||||
all_dest_files = set([dest_name for dest_name, src_name, typecode in toc])
|
||||
|
||||
# Process the TOC to create SYMLINK entries
|
||||
new_toc = []
|
||||
for entry in toc:
|
||||
dest_name, src_name, typecode = entry
|
||||
|
||||
# Skip entries that are already symbolic links
|
||||
if typecode == 'SYMLINK':
|
||||
new_toc.append(entry)
|
||||
continue
|
||||
|
||||
# Skip entries without valid source name (e.g., OPTION)
|
||||
if not src_name:
|
||||
new_toc.append(entry)
|
||||
continue
|
||||
|
||||
# Source path is not a symbolic link (i.e., it is a regular file or directory)
|
||||
if not os.path.islink(src_name):
|
||||
new_toc.append(entry)
|
||||
continue
|
||||
|
||||
# Try preserving the symbolic link, under strict relative-relationship-preservation check
|
||||
symlink_entry = _try_preserving_symbolic_link(dest_name, src_name, all_dest_files)
|
||||
|
||||
if symlink_entry:
|
||||
new_toc.append(symlink_entry)
|
||||
else:
|
||||
new_toc.append(entry)
|
||||
|
||||
return new_toc
|
||||
|
||||
|
||||
def _try_preserving_symbolic_link(dest_name, src_name, all_dest_files):
|
||||
seen_src_files = set()
|
||||
|
||||
# Set initial values for the loop
|
||||
ref_src_file = src_name
|
||||
ref_dest_file = dest_name
|
||||
|
||||
while True:
|
||||
# Guard against cyclic links...
|
||||
if ref_src_file in seen_src_files:
|
||||
break
|
||||
seen_src_files.add(ref_src_file)
|
||||
|
||||
# Stop when referenced source file is not a symbolic link anymore.
|
||||
if not os.path.islink(ref_src_file):
|
||||
break
|
||||
|
||||
# Read the symbolic link's target, but do not fully resolve it using os.path.realpath(), because there might be
|
||||
# other symbolic links involved as well (for example, /lib64 -> /usr/lib64 whereas we are processing
|
||||
# /lib64/liba.so -> /lib64/liba.so.1)
|
||||
symlink_target = os.readlink(ref_src_file)
|
||||
if os.path.isabs(symlink_target):
|
||||
break # We support only relative symbolic links.
|
||||
|
||||
ref_dest_file = os.path.join(os.path.dirname(ref_dest_file), symlink_target)
|
||||
ref_dest_file = os.path.normpath(ref_dest_file) # remove any '..'
|
||||
|
||||
ref_src_file = os.path.join(os.path.dirname(ref_src_file), symlink_target)
|
||||
ref_src_file = os.path.normpath(ref_src_file) # remove any '..'
|
||||
|
||||
# Check if referenced destination file is valid (i.e., we are collecting a file under referenced name).
|
||||
if ref_dest_file in all_dest_files:
|
||||
# Sanity check: original source name and current referenced source name must, after complete resolution,
|
||||
# point to the same file.
|
||||
if os.path.realpath(src_name) == os.path.realpath(ref_src_file):
|
||||
# Compute relative link for the destination file (might be modified, if we went over non-collected
|
||||
# intermediate links).
|
||||
rel_link = os.path.relpath(ref_dest_file, os.path.dirname(dest_name))
|
||||
return dest_name, rel_link, 'SYMLINK'
|
||||
|
||||
# If referenced destination is not valid, do another iteration in case we are dealing with chained links and we
|
||||
# are not collecting an intermediate link...
|
||||
|
||||
return None
|
||||
@@ -0,0 +1,90 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2022-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
|
||||
def normalize_icon_type(icon_path: str, allowed_types: Tuple[str], convert_type: str, workpath: str) -> str:
|
||||
"""
|
||||
Returns a valid icon path or raises an Exception on error.
|
||||
Ensures that the icon exists, and, if necessary, attempts to convert it to correct OS-specific format using Pillow.
|
||||
|
||||
Takes:
|
||||
icon_path - the icon given by the user
|
||||
allowed_types - a tuple of icon formats that should be allowed through
|
||||
EX: ("ico", "exe")
|
||||
convert_type - the type to attempt conversion too if necessary
|
||||
EX: "icns"
|
||||
workpath - the temp directory to save any newly generated image files
|
||||
"""
|
||||
|
||||
# explicitly error if file not found
|
||||
if not os.path.exists(icon_path):
|
||||
raise FileNotFoundError(f"Icon input file {icon_path} not found")
|
||||
|
||||
_, extension = os.path.splitext(icon_path)
|
||||
extension = extension[1:] # get rid of the "." in ".whatever"
|
||||
|
||||
# if the file is already in the right format, pass it back unchanged
|
||||
if extension in allowed_types:
|
||||
# Check both the suffix and the header of the file to guard against the user confusing image types.
|
||||
signatures = hex_signatures[extension]
|
||||
with open(icon_path, "rb") as f:
|
||||
header = f.read(max(len(s) for s in signatures))
|
||||
if any(list(header)[:len(s)] == s for s in signatures):
|
||||
return icon_path
|
||||
|
||||
# The icon type is wrong! Let's try and import PIL
|
||||
try:
|
||||
from PIL import Image as PILImage
|
||||
import PIL
|
||||
|
||||
except ImportError:
|
||||
raise ValueError(
|
||||
f"Received icon image '{icon_path}' which exists but is not in the correct format. On this platform, "
|
||||
f"only {allowed_types} images may be used as icons. If Pillow is installed, automatic conversion will "
|
||||
f"be attempted. Please install Pillow or convert your '{extension}' file to one of {allowed_types} "
|
||||
f"and try again."
|
||||
)
|
||||
|
||||
# Let's try to use PIL to convert the icon file type
|
||||
try:
|
||||
_generated_name = f"generated-{hashlib.sha256(icon_path.encode()).hexdigest()}.{convert_type}"
|
||||
generated_icon = os.path.join(workpath, _generated_name)
|
||||
with PILImage.open(icon_path) as im:
|
||||
# If an image uses a custom palette + transparency, convert it to RGBA for a better alpha mask depth.
|
||||
if im.mode == "P" and im.info.get("transparency", None) is not None:
|
||||
# The bit depth of the alpha channel will be higher, and the images will look better when eventually
|
||||
# scaled to multiple sizes (16,24,32,..) for the ICO format for example.
|
||||
im = im.convert("RGBA")
|
||||
im.save(generated_icon)
|
||||
icon_path = generated_icon
|
||||
except PIL.UnidentifiedImageError:
|
||||
raise ValueError(
|
||||
f"Something went wrong converting icon image '{icon_path}' to '.{convert_type}' with Pillow, "
|
||||
f"perhaps the image format is unsupported. Try again with a different file or use a file that can "
|
||||
f"be used without conversion on this platform: {allowed_types}"
|
||||
)
|
||||
|
||||
return icon_path
|
||||
|
||||
|
||||
# Possible initial bytes of icon types PyInstaller needs to be able to recognise.
|
||||
# Taken from: https://en.wikipedia.org/wiki/List_of_file_signatures
|
||||
hex_signatures = {
|
||||
"png": [[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]],
|
||||
"exe": [[0x4D, 0x5A], [0x5A, 0x4D]],
|
||||
"ico": [[0x00, 0x00, 0x01, 0x00]],
|
||||
"icns": [[0x69, 0x63, 0x6e, 0x73]],
|
||||
}
|
||||
@@ -0,0 +1,923 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Automatically build spec files containing a description of the project.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from PyInstaller import DEFAULT_SPECPATH, HOMEPATH
|
||||
from PyInstaller import log as logging
|
||||
from PyInstaller.building.templates import bundleexetmplt, bundletmplt, onedirtmplt, onefiletmplt, splashtmpl
|
||||
from PyInstaller.compat import is_darwin, is_win
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# This list gives valid choices for the ``--debug`` command-line option, except for the ``all`` choice.
|
||||
DEBUG_ARGUMENT_CHOICES = ['imports', 'bootloader', 'noarchive']
|
||||
# This is the ``all`` choice.
|
||||
DEBUG_ALL_CHOICE = ['all']
|
||||
|
||||
|
||||
def escape_win_filepath(path):
|
||||
# escape all \ with another \ after using normpath to clean up the path
|
||||
return os.path.normpath(path).replace('\\', '\\\\')
|
||||
|
||||
|
||||
def make_path_spec_relative(filename, spec_dir):
|
||||
"""
|
||||
Make the filename relative to the directory containing .spec file if filename is relative and not absolute.
|
||||
Otherwise keep filename untouched.
|
||||
"""
|
||||
if os.path.isabs(filename):
|
||||
return filename
|
||||
else:
|
||||
filename = os.path.abspath(filename)
|
||||
# Make it relative.
|
||||
filename = os.path.relpath(filename, start=spec_dir)
|
||||
return filename
|
||||
|
||||
|
||||
# Support for trying to avoid hard-coded paths in the .spec files. Eg, all files rooted in the Installer directory tree
|
||||
# will be written using "HOMEPATH", thus allowing this spec file to be used with any Installer installation. Same thing
|
||||
# could be done for other paths too.
|
||||
path_conversions = ((HOMEPATH, "HOMEPATH"),)
|
||||
|
||||
|
||||
class SourceDestAction(argparse.Action):
|
||||
"""
|
||||
A command line option which takes multiple source:dest pairs.
|
||||
"""
|
||||
def __init__(self, *args, default=None, metavar=None, **kwargs):
|
||||
super().__init__(*args, default=[], metavar='SOURCE:DEST', **kwargs)
|
||||
|
||||
def __call__(self, parser, namespace, value, option_string=None):
|
||||
try:
|
||||
# Find the only separator that isn't a Windows drive.
|
||||
separator, = (m for m in re.finditer(rf"(^\w:[/\\])|[:{os.pathsep}]", value) if not m[1])
|
||||
except ValueError:
|
||||
# Split into SRC and DEST failed, wrong syntax
|
||||
raise argparse.ArgumentError(self, f'Wrong syntax, should be {self.option_strings[0]}=SOURCE:DEST')
|
||||
src = value[:separator.start()]
|
||||
dest = value[separator.end():]
|
||||
if not src or not dest:
|
||||
# Syntax was correct, but one or both of SRC and DEST was not given
|
||||
raise argparse.ArgumentError(self, "You have to specify both SOURCE and DEST")
|
||||
|
||||
# argparse is not particularly smart with copy by reference typed defaults. If the current list is the default,
|
||||
# replace it before modifying it to avoid changing the default.
|
||||
if getattr(namespace, self.dest) is self.default:
|
||||
setattr(namespace, self.dest, [])
|
||||
getattr(namespace, self.dest).append((src, dest))
|
||||
|
||||
|
||||
def make_variable_path(filename, conversions=path_conversions):
|
||||
if not os.path.isabs(filename):
|
||||
# os.path.commonpath can not compare relative and absolute paths, and if filename is not absolute, none of the
|
||||
# paths in conversions will match anyway.
|
||||
return None, filename
|
||||
for (from_path, to_name) in conversions:
|
||||
assert os.path.abspath(from_path) == from_path, ("path '%s' should already be absolute" % from_path)
|
||||
try:
|
||||
common_path = os.path.commonpath([filename, from_path])
|
||||
except ValueError:
|
||||
# Per https://docs.python.org/3/library/os.path.html#os.path.commonpath, this raises ValueError in several
|
||||
# cases which prevent computing a common path.
|
||||
common_path = None
|
||||
if common_path == from_path:
|
||||
rest = filename[len(from_path):]
|
||||
if rest.startswith(('\\', '/')):
|
||||
rest = rest[1:]
|
||||
return to_name, rest
|
||||
return None, filename
|
||||
|
||||
|
||||
def removed_key_option(x):
|
||||
from PyInstaller.exceptions import RemovedCipherFeatureError
|
||||
raise RemovedCipherFeatureError("Please remove your --key=xxx argument.")
|
||||
|
||||
|
||||
class _RemovedFlagAction(argparse.Action):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs["help"] = argparse.SUPPRESS
|
||||
kwargs["nargs"] = 0
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class _RemovedNoEmbedManifestAction(_RemovedFlagAction):
|
||||
def __call__(self, *args, **kwargs):
|
||||
from PyInstaller.exceptions import RemovedExternalManifestError
|
||||
raise RemovedExternalManifestError("Please remove your --no-embed-manifest argument.")
|
||||
|
||||
|
||||
class _RemovedWinPrivateAssembliesAction(_RemovedFlagAction):
|
||||
def __call__(self, *args, **kwargs):
|
||||
from PyInstaller.exceptions import RemovedWinSideBySideSupportError
|
||||
raise RemovedWinSideBySideSupportError("Please remove your --win-private-assemblies argument.")
|
||||
|
||||
|
||||
class _RemovedWinNoPreferRedirectsAction(_RemovedFlagAction):
|
||||
def __call__(self, *args, **kwargs):
|
||||
from PyInstaller.exceptions import RemovedWinSideBySideSupportError
|
||||
raise RemovedWinSideBySideSupportError("Please remove your --win-no-prefer-redirects argument.")
|
||||
|
||||
|
||||
# An object used in place of a "path string", which knows how to repr() itself using variable names instead of
|
||||
# hard-coded paths.
|
||||
class Path:
|
||||
def __init__(self, *parts):
|
||||
self.path = os.path.join(*parts)
|
||||
self.variable_prefix = self.filename_suffix = None
|
||||
|
||||
def __repr__(self):
|
||||
if self.filename_suffix is None:
|
||||
self.variable_prefix, self.filename_suffix = make_variable_path(self.path)
|
||||
if self.variable_prefix is None:
|
||||
return repr(self.path)
|
||||
return "os.path.join(" + self.variable_prefix + "," + repr(self.filename_suffix) + ")"
|
||||
|
||||
|
||||
# An object used to construct extra preamble for the spec file, in order to accommodate extra collect_*() calls from the
|
||||
# command-line
|
||||
class Preamble:
|
||||
def __init__(
|
||||
self, datas, binaries, hiddenimports, collect_data, collect_binaries, collect_submodules, collect_all,
|
||||
copy_metadata, recursive_copy_metadata
|
||||
):
|
||||
# Initialize with literal values - will be switched to preamble variable name later, if necessary
|
||||
self.binaries = binaries or []
|
||||
self.hiddenimports = hiddenimports or []
|
||||
self.datas = datas or []
|
||||
# Preamble content
|
||||
self.content = []
|
||||
|
||||
# Import statements
|
||||
if collect_data:
|
||||
self._add_hookutil_import('collect_data_files')
|
||||
if collect_binaries:
|
||||
self._add_hookutil_import('collect_dynamic_libs')
|
||||
if collect_submodules:
|
||||
self._add_hookutil_import('collect_submodules')
|
||||
if collect_all:
|
||||
self._add_hookutil_import('collect_all')
|
||||
if copy_metadata or recursive_copy_metadata:
|
||||
self._add_hookutil_import('copy_metadata')
|
||||
if self.content:
|
||||
self.content += [''] # empty line to separate the section
|
||||
# Variables
|
||||
if collect_data or copy_metadata or collect_all or recursive_copy_metadata:
|
||||
self._add_var('datas', self.datas)
|
||||
self.datas = 'datas' # switch to variable
|
||||
if collect_binaries or collect_all:
|
||||
self._add_var('binaries', self.binaries)
|
||||
self.binaries = 'binaries' # switch to variable
|
||||
if collect_submodules or collect_all:
|
||||
self._add_var('hiddenimports', self.hiddenimports)
|
||||
self.hiddenimports = 'hiddenimports' # switch to variable
|
||||
# Content - collect_data_files
|
||||
for entry in collect_data:
|
||||
self._add_collect_data(entry)
|
||||
# Content - copy_metadata
|
||||
for entry in copy_metadata:
|
||||
self._add_copy_metadata(entry)
|
||||
# Content - copy_metadata(..., recursive=True)
|
||||
for entry in recursive_copy_metadata:
|
||||
self._add_recursive_copy_metadata(entry)
|
||||
# Content - collect_binaries
|
||||
for entry in collect_binaries:
|
||||
self._add_collect_binaries(entry)
|
||||
# Content - collect_submodules
|
||||
for entry in collect_submodules:
|
||||
self._add_collect_submodules(entry)
|
||||
# Content - collect_all
|
||||
for entry in collect_all:
|
||||
self._add_collect_all(entry)
|
||||
# Merge
|
||||
if self.content and self.content[-1] != '':
|
||||
self.content += [''] # empty line
|
||||
self.content = '\n'.join(self.content)
|
||||
|
||||
def _add_hookutil_import(self, name):
|
||||
self.content += ['from PyInstaller.utils.hooks import {0}'.format(name)]
|
||||
|
||||
def _add_var(self, name, initial_value):
|
||||
self.content += ['{0} = {1}'.format(name, initial_value)]
|
||||
|
||||
def _add_collect_data(self, name):
|
||||
self.content += ['datas += collect_data_files(\'{0}\')'.format(name)]
|
||||
|
||||
def _add_copy_metadata(self, name):
|
||||
self.content += ['datas += copy_metadata(\'{0}\')'.format(name)]
|
||||
|
||||
def _add_recursive_copy_metadata(self, name):
|
||||
self.content += ['datas += copy_metadata(\'{0}\', recursive=True)'.format(name)]
|
||||
|
||||
def _add_collect_binaries(self, name):
|
||||
self.content += ['binaries += collect_dynamic_libs(\'{0}\')'.format(name)]
|
||||
|
||||
def _add_collect_submodules(self, name):
|
||||
self.content += ['hiddenimports += collect_submodules(\'{0}\')'.format(name)]
|
||||
|
||||
def _add_collect_all(self, name):
|
||||
self.content += [
|
||||
'tmp_ret = collect_all(\'{0}\')'.format(name),
|
||||
'datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]'
|
||||
]
|
||||
|
||||
|
||||
def __add_options(parser):
|
||||
"""
|
||||
Add the `Makespec` options to a option-parser instance or a option group.
|
||||
"""
|
||||
g = parser.add_argument_group('What to generate')
|
||||
g.add_argument(
|
||||
"-D",
|
||||
"--onedir",
|
||||
dest="onefile",
|
||||
action="store_false",
|
||||
default=None,
|
||||
help="Create a one-folder bundle containing an executable (default)",
|
||||
)
|
||||
g.add_argument(
|
||||
"-F",
|
||||
"--onefile",
|
||||
dest="onefile",
|
||||
action="store_true",
|
||||
default=None,
|
||||
help="Create a one-file bundled executable.",
|
||||
)
|
||||
g.add_argument(
|
||||
"--specpath",
|
||||
metavar="DIR",
|
||||
help="Folder to store the generated spec file (default: current directory)",
|
||||
)
|
||||
g.add_argument(
|
||||
"-n",
|
||||
"--name",
|
||||
help="Name to assign to the bundled app and spec file (default: first script's basename)",
|
||||
)
|
||||
g.add_argument(
|
||||
"--contents-directory",
|
||||
help="For onedir builds only, specify the name of the directory in which all supporting files (i.e. everything "
|
||||
"except the executable itself) will be placed in. Use \".\" to re-enable old onedir layout without contents "
|
||||
"directory.",
|
||||
)
|
||||
|
||||
g = parser.add_argument_group('What to bundle, where to search')
|
||||
g.add_argument(
|
||||
'--add-data',
|
||||
action=SourceDestAction,
|
||||
dest='datas',
|
||||
help="Additional data files or directories containing data files to be added to the application. The argument "
|
||||
'value should be in form of "source:dest_dir", where source is the path to file (or directory) to be '
|
||||
"collected, dest_dir is the destination directory relative to the top-level application directory, and both "
|
||||
"paths are separated by a colon (:). To put a file in the top-level application directory, use . as a "
|
||||
"dest_dir. This option can be used multiple times."
|
||||
)
|
||||
g.add_argument(
|
||||
'--add-binary',
|
||||
action=SourceDestAction,
|
||||
dest="binaries",
|
||||
help='Additional binary files to be added to the executable. See the ``--add-data`` option for the format. '
|
||||
'This option can be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
"-p",
|
||||
"--paths",
|
||||
dest="pathex",
|
||||
metavar="DIR",
|
||||
action="append",
|
||||
default=[],
|
||||
help="A path to search for imports (like using PYTHONPATH). Multiple paths are allowed, separated by ``%s``, "
|
||||
"or use this option multiple times. Equivalent to supplying the ``pathex`` argument in the spec file." %
|
||||
repr(os.pathsep),
|
||||
)
|
||||
g.add_argument(
|
||||
'--hidden-import',
|
||||
'--hiddenimport',
|
||||
action='append',
|
||||
default=[],
|
||||
metavar="MODULENAME",
|
||||
dest='hiddenimports',
|
||||
help='Name an import not visible in the code of the script(s). This option can be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--collect-submodules',
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="MODULENAME",
|
||||
dest='collect_submodules',
|
||||
help='Collect all submodules from the specified package or module. This option can be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--collect-data',
|
||||
'--collect-datas',
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="MODULENAME",
|
||||
dest='collect_data',
|
||||
help='Collect all data from the specified package or module. This option can be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--collect-binaries',
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="MODULENAME",
|
||||
dest='collect_binaries',
|
||||
help='Collect all binaries from the specified package or module. This option can be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--collect-all',
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="MODULENAME",
|
||||
dest='collect_all',
|
||||
help='Collect all submodules, data files, and binaries from the specified package or module. This option can '
|
||||
'be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--copy-metadata',
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="PACKAGENAME",
|
||||
dest='copy_metadata',
|
||||
help='Copy metadata for the specified package. This option can be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--recursive-copy-metadata',
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="PACKAGENAME",
|
||||
dest='recursive_copy_metadata',
|
||||
help='Copy metadata for the specified package and all its dependencies. This option can be used multiple '
|
||||
'times.',
|
||||
)
|
||||
g.add_argument(
|
||||
"--additional-hooks-dir",
|
||||
action="append",
|
||||
dest="hookspath",
|
||||
default=[],
|
||||
help="An additional path to search for hooks. This option can be used multiple times.",
|
||||
)
|
||||
g.add_argument(
|
||||
'--runtime-hook',
|
||||
action='append',
|
||||
dest='runtime_hooks',
|
||||
default=[],
|
||||
help='Path to a custom runtime hook file. A runtime hook is code that is bundled with the executable and is '
|
||||
'executed before any other code or module to set up special features of the runtime environment. This option '
|
||||
'can be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--exclude-module',
|
||||
dest='excludes',
|
||||
action='append',
|
||||
default=[],
|
||||
help='Optional module or package (the Python name, not the path name) that will be ignored (as though it was '
|
||||
'not found). This option can be used multiple times.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--key',
|
||||
dest='key',
|
||||
help=argparse.SUPPRESS,
|
||||
type=removed_key_option,
|
||||
)
|
||||
g.add_argument(
|
||||
'--splash',
|
||||
dest='splash',
|
||||
metavar="IMAGE_FILE",
|
||||
help="(EXPERIMENTAL) Add an splash screen with the image IMAGE_FILE to the application. The splash screen can "
|
||||
"display progress updates while unpacking.",
|
||||
)
|
||||
g.add_argument(
|
||||
'--splash-center',
|
||||
dest='splash_center',
|
||||
default=None,
|
||||
choices={'default', 'primary', 'virtual', 'active'},
|
||||
help="Splash screen centering mode. See the splash screen documentation for details.",
|
||||
)
|
||||
|
||||
g = parser.add_argument_group('How to generate')
|
||||
g.add_argument(
|
||||
"-d",
|
||||
"--debug",
|
||||
# If this option is not specified, then its default value is an empty list (no debug options selected).
|
||||
default=[],
|
||||
# Note that ``nargs`` is omitted. This produces a single item not stored in a list, as opposed to a list
|
||||
# containing one item, as per `nargs <https://docs.python.org/3/library/argparse.html#nargs>`_.
|
||||
nargs=None,
|
||||
# The options specified must come from this list.
|
||||
choices=DEBUG_ALL_CHOICE + DEBUG_ARGUMENT_CHOICES,
|
||||
# Append choice, rather than storing them (which would overwrite any previous selections).
|
||||
action='append',
|
||||
# Allow newlines in the help text; see the ``_SmartFormatter`` in ``__main__.py``.
|
||||
help=(
|
||||
"R|Provide assistance with debugging a frozen\n"
|
||||
"application. This argument may be provided multiple\n"
|
||||
"times to select several of the following options.\n"
|
||||
"\n"
|
||||
"- all: All three of the following options.\n"
|
||||
"\n"
|
||||
"- imports: specify the -v option to the underlying\n"
|
||||
" Python interpreter, causing it to print a message\n"
|
||||
" each time a module is initialized, showing the\n"
|
||||
" place (filename or built-in module) from which it\n"
|
||||
" is loaded. See\n"
|
||||
" https://docs.python.org/3/using/cmdline.html#id4.\n"
|
||||
"\n"
|
||||
"- bootloader: tell the bootloader to issue progress\n"
|
||||
" messages while initializing and starting the\n"
|
||||
" bundled app. Used to diagnose problems with\n"
|
||||
" missing imports.\n"
|
||||
"\n"
|
||||
"- noarchive: instead of storing all frozen Python\n"
|
||||
" source files as an archive inside the resulting\n"
|
||||
" executable, store them as files in the resulting\n"
|
||||
" output directory.\n"
|
||||
"\n"
|
||||
),
|
||||
)
|
||||
g.add_argument(
|
||||
'--optimize',
|
||||
dest='optimize',
|
||||
metavar='LEVEL',
|
||||
type=int,
|
||||
choices={-1, 0, 1, 2},
|
||||
default=None,
|
||||
help='Bytecode optimization level used for collected python modules and scripts. For details, see the section '
|
||||
'“Bytecode Optimization Level” in PyInstaller manual.',
|
||||
)
|
||||
g.add_argument(
|
||||
'--python-option',
|
||||
dest='python_options',
|
||||
metavar='PYTHON_OPTION',
|
||||
action='append',
|
||||
default=[],
|
||||
help='Specify a command-line option to pass to the Python interpreter at runtime. Currently supports '
|
||||
'"v" (equivalent to "--debug imports"), "u", "W <warning control>", "X <xoption>", and "hash_seed=<value>". '
|
||||
'For details, see the section "Specifying Python Interpreter Options" in PyInstaller manual.',
|
||||
)
|
||||
g.add_argument(
|
||||
"-s",
|
||||
"--strip",
|
||||
action="store_true",
|
||||
help="Apply a symbol-table strip to the executable and shared libs (not recommended for Windows)",
|
||||
)
|
||||
g.add_argument(
|
||||
"--noupx",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Do not use UPX even if it is available (works differently between Windows and *nix)",
|
||||
)
|
||||
g.add_argument(
|
||||
"--upx-exclude",
|
||||
dest="upx_exclude",
|
||||
metavar="FILE",
|
||||
action="append",
|
||||
help="Prevent a binary from being compressed when using upx. This is typically used if upx corrupts certain "
|
||||
"binaries during compression. FILE is the filename of the binary without path. This option can be used "
|
||||
"multiple times.",
|
||||
)
|
||||
|
||||
g = parser.add_argument_group('Windows and macOS specific options')
|
||||
g.add_argument(
|
||||
"-c",
|
||||
"--console",
|
||||
"--nowindowed",
|
||||
dest="console",
|
||||
action="store_true",
|
||||
default=None,
|
||||
help="Open a console window for standard i/o (default). On Windows this option has no effect if the first "
|
||||
"script is a '.pyw' file.",
|
||||
)
|
||||
g.add_argument(
|
||||
"-w",
|
||||
"--windowed",
|
||||
"--noconsole",
|
||||
dest="console",
|
||||
action="store_false",
|
||||
default=None,
|
||||
help="Windows and macOS: do not provide a console window for standard i/o. On macOS this also triggers "
|
||||
"building a macOS .app bundle. On Windows this option is automatically set if the first script is a '.pyw' "
|
||||
"file. This option is ignored on *NIX systems.",
|
||||
)
|
||||
g.add_argument(
|
||||
"--hide-console",
|
||||
type=str,
|
||||
choices={'hide-early', 'hide-late', 'minimize-early', 'minimize-late'},
|
||||
default=None,
|
||||
help="Windows only: in console-enabled executable, have bootloader automatically hide or minimize the console "
|
||||
"window if the program owns the console window (i.e., was not launched from an existing console window).",
|
||||
)
|
||||
g.add_argument(
|
||||
"-i",
|
||||
"--icon",
|
||||
action='append',
|
||||
dest="icon_file",
|
||||
metavar='<FILE.ico or FILE.exe,ID or FILE.icns or Image or "NONE">',
|
||||
help="FILE.ico: apply the icon to a Windows executable. FILE.exe,ID: extract the icon with ID from an exe. "
|
||||
"FILE.icns: apply the icon to the .app bundle on macOS. If an image file is entered that isn't in the "
|
||||
"platform format (ico on Windows, icns on Mac), PyInstaller tries to use Pillow to translate the icon into "
|
||||
"the correct format (if Pillow is installed). Use \"NONE\" to not apply any icon, thereby making the OS show "
|
||||
"some default (default: apply PyInstaller's icon). This option can be used multiple times.",
|
||||
)
|
||||
g.add_argument(
|
||||
"--disable-windowed-traceback",
|
||||
dest="disable_windowed_traceback",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Disable traceback dump of unhandled exception in windowed (noconsole) mode (Windows and macOS only), "
|
||||
"and instead display a message that this feature is disabled.",
|
||||
)
|
||||
|
||||
g = parser.add_argument_group('Windows specific options')
|
||||
g.add_argument(
|
||||
"--version-file",
|
||||
dest="version_file",
|
||||
metavar="FILE",
|
||||
help="Add a version resource from FILE to the exe.",
|
||||
)
|
||||
g.add_argument(
|
||||
"--manifest",
|
||||
metavar="<FILE or XML>",
|
||||
help="Add manifest FILE or XML to the exe.",
|
||||
)
|
||||
g.add_argument(
|
||||
"-m",
|
||||
dest="shorthand_manifest",
|
||||
metavar="<FILE or XML>",
|
||||
help="Deprecated shorthand for --manifest.",
|
||||
)
|
||||
g.add_argument(
|
||||
"--no-embed-manifest",
|
||||
action=_RemovedNoEmbedManifestAction,
|
||||
)
|
||||
g.add_argument(
|
||||
"-r",
|
||||
"--resource",
|
||||
dest="resources",
|
||||
metavar="RESOURCE",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Add or update a resource to a Windows executable. The RESOURCE is one to four items, "
|
||||
"FILE[,TYPE[,NAME[,LANGUAGE]]]. FILE can be a data file or an exe/dll. For data files, at least TYPE and NAME "
|
||||
"must be specified. LANGUAGE defaults to 0 or may be specified as wildcard * to update all resources of the "
|
||||
"given TYPE and NAME. For exe/dll files, all resources from FILE will be added/updated to the final executable "
|
||||
"if TYPE, NAME and LANGUAGE are omitted or specified as wildcard *. This option can be used multiple times.",
|
||||
)
|
||||
g.add_argument(
|
||||
'--uac-admin',
|
||||
dest='uac_admin',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Using this option creates a Manifest that will request elevation upon application start.",
|
||||
)
|
||||
g.add_argument(
|
||||
'--uac-uiaccess',
|
||||
dest='uac_uiaccess',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Using this option allows an elevated application to work with Remote Desktop.",
|
||||
)
|
||||
|
||||
g = parser.add_argument_group('Windows Side-by-side Assembly searching options (advanced)')
|
||||
g.add_argument(
|
||||
"--win-private-assemblies",
|
||||
action=_RemovedWinPrivateAssembliesAction,
|
||||
)
|
||||
g.add_argument(
|
||||
"--win-no-prefer-redirects",
|
||||
action=_RemovedWinNoPreferRedirectsAction,
|
||||
)
|
||||
|
||||
g = parser.add_argument_group('macOS specific options')
|
||||
g.add_argument(
|
||||
"--argv-emulation",
|
||||
dest="argv_emulation",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Enable argv emulation for macOS app bundles. If enabled, the initial open document/URL event is "
|
||||
"processed by the bootloader and the passed file paths or URLs are appended to sys.argv.",
|
||||
)
|
||||
|
||||
g.add_argument(
|
||||
'--osx-bundle-identifier',
|
||||
dest='bundle_identifier',
|
||||
help="macOS .app bundle identifier is used as the default unique program name for code signing purposes. "
|
||||
"The usual form is a hierarchical name in reverse DNS notation. For example: com.mycompany.department.appname "
|
||||
"(default: first script's basename)",
|
||||
)
|
||||
|
||||
g.add_argument(
|
||||
'--target-architecture',
|
||||
'--target-arch',
|
||||
dest='target_arch',
|
||||
metavar='ARCH',
|
||||
default=None,
|
||||
help="Target architecture (macOS only; valid values: x86_64, arm64, universal2). Enables switching between "
|
||||
"universal2 and single-arch version of frozen application (provided python installation supports the target "
|
||||
"architecture). If not target architecture is not specified, the current running architecture is targeted.",
|
||||
)
|
||||
|
||||
g.add_argument(
|
||||
'--codesign-identity',
|
||||
dest='codesign_identity',
|
||||
metavar='IDENTITY',
|
||||
default=None,
|
||||
help="Code signing identity (macOS only). Use the provided identity to sign collected binaries and generated "
|
||||
"executable. If signing identity is not provided, ad-hoc signing is performed instead.",
|
||||
)
|
||||
|
||||
g.add_argument(
|
||||
'--osx-entitlements-file',
|
||||
dest='entitlements_file',
|
||||
metavar='FILENAME',
|
||||
default=None,
|
||||
help="Entitlements file to use when code-signing the collected binaries (macOS only).",
|
||||
)
|
||||
|
||||
g = parser.add_argument_group('Rarely used special options')
|
||||
g.add_argument(
|
||||
"--runtime-tmpdir",
|
||||
dest="runtime_tmpdir",
|
||||
metavar="PATH",
|
||||
help="Where to extract libraries and support files in `onefile` mode. If this option is given, the bootloader "
|
||||
"will ignore any temp-folder location defined by the run-time OS. The ``_MEIxxxxxx``-folder will be created "
|
||||
"here. Please use this option only if you know what you are doing. Note that on POSIX systems, PyInstaller's "
|
||||
"bootloader does NOT perform shell-style environment variable expansion on the given path string. Therefore, "
|
||||
"using environment variables (e.g., ``~`` or ``$HOME``) in path will NOT work.",
|
||||
)
|
||||
g.add_argument(
|
||||
"--bootloader-ignore-signals",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Tell the bootloader to ignore signals rather than forwarding them to the child process. Useful in "
|
||||
"situations where for example a supervisor process signals both the bootloader and the child (e.g., via a "
|
||||
"process group) to avoid signalling the child twice.",
|
||||
)
|
||||
|
||||
|
||||
def main(
|
||||
scripts,
|
||||
name=None,
|
||||
onefile=False,
|
||||
console=True,
|
||||
debug=[],
|
||||
python_options=[],
|
||||
strip=False,
|
||||
noupx=False,
|
||||
upx_exclude=None,
|
||||
runtime_tmpdir=None,
|
||||
contents_directory=None,
|
||||
pathex=[],
|
||||
version_file=None,
|
||||
specpath=None,
|
||||
bootloader_ignore_signals=False,
|
||||
disable_windowed_traceback=False,
|
||||
datas=[],
|
||||
binaries=[],
|
||||
icon_file=None,
|
||||
manifest=None,
|
||||
resources=[],
|
||||
bundle_identifier=None,
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
uac_admin=False,
|
||||
uac_uiaccess=False,
|
||||
collect_submodules=[],
|
||||
collect_binaries=[],
|
||||
collect_data=[],
|
||||
collect_all=[],
|
||||
copy_metadata=[],
|
||||
splash=None,
|
||||
recursive_copy_metadata=[],
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
argv_emulation=False,
|
||||
hide_console=None,
|
||||
optimize=None,
|
||||
splash_center=None,
|
||||
**_kwargs
|
||||
):
|
||||
# Default values for onefile and console when not explicitly specified on command-line (indicated by None)
|
||||
if onefile is None:
|
||||
onefile = False
|
||||
|
||||
if console is None:
|
||||
console = True
|
||||
|
||||
# If appname is not specified - use the basename of the main script as name.
|
||||
if name is None:
|
||||
name = os.path.splitext(os.path.basename(scripts[0]))[0]
|
||||
|
||||
# If specpath not specified - use default value - current working directory.
|
||||
if specpath is None:
|
||||
specpath = DEFAULT_SPECPATH
|
||||
else:
|
||||
# Expand starting tilde into user's home directory, as a work-around for tilde not being expanded by shell when
|
||||
# using `--specpath=~/path/abc` instead of `--specpath ~/path/abc` (or when the path argument is quoted).
|
||||
specpath = os.path.expanduser(specpath)
|
||||
# If cwd is the root directory of PyInstaller, generate the .spec file in ./appname/ subdirectory.
|
||||
if specpath == HOMEPATH:
|
||||
specpath = os.path.join(HOMEPATH, name)
|
||||
# Create directory tree if missing.
|
||||
if not os.path.exists(specpath):
|
||||
os.makedirs(specpath)
|
||||
|
||||
# Handle additional EXE options.
|
||||
exe_options = ''
|
||||
if version_file:
|
||||
exe_options += "\n version='%s'," % escape_win_filepath(version_file)
|
||||
if uac_admin:
|
||||
exe_options += "\n uac_admin=True,"
|
||||
if uac_uiaccess:
|
||||
exe_options += "\n uac_uiaccess=True,"
|
||||
if icon_file:
|
||||
# Icon file for Windows.
|
||||
# On Windows, the default icon is embedded in the bootloader executable.
|
||||
if icon_file[0] == 'NONE':
|
||||
exe_options += "\n icon='NONE',"
|
||||
else:
|
||||
exe_options += "\n icon=[%s]," % ','.join("'%s'" % escape_win_filepath(ic) for ic in icon_file)
|
||||
# Icon file for macOS.
|
||||
# We need to encapsulate it into apostrofes.
|
||||
icon_file = "'%s'" % icon_file[0]
|
||||
else:
|
||||
# On macOS, the default icon has to be copied into the .app bundle.
|
||||
# The the text value 'None' means - use default icon.
|
||||
icon_file = 'None'
|
||||
if contents_directory:
|
||||
exe_options += "\n contents_directory='%s'," % (contents_directory or "_internal")
|
||||
if hide_console:
|
||||
exe_options += "\n hide_console='%s'," % hide_console
|
||||
|
||||
if bundle_identifier:
|
||||
# We need to encapsulate it into apostrofes.
|
||||
bundle_identifier = "'%s'" % bundle_identifier
|
||||
|
||||
if _kwargs["shorthand_manifest"]:
|
||||
manifest = _kwargs["shorthand_manifest"]
|
||||
logger.log(
|
||||
logging.DEPRECATION, "PyInstaller v7 will remove the -m shorthand flag. Please use --manifest=%s instead",
|
||||
manifest
|
||||
)
|
||||
if manifest:
|
||||
if "<" in manifest:
|
||||
# Assume XML string
|
||||
exe_options += "\n manifest='%s'," % manifest.replace("'", "\\'")
|
||||
else:
|
||||
# Assume filename
|
||||
exe_options += "\n manifest='%s'," % escape_win_filepath(manifest)
|
||||
if resources:
|
||||
resources = list(map(escape_win_filepath, resources))
|
||||
exe_options += "\n resources=%s," % repr(resources)
|
||||
|
||||
hiddenimports = hiddenimports or []
|
||||
upx_exclude = upx_exclude or []
|
||||
|
||||
if is_darwin and onefile and not console:
|
||||
from PyInstaller.building.osx import WINDOWED_ONEFILE_DEPRCATION
|
||||
logger.log(logging.DEPRECATION, WINDOWED_ONEFILE_DEPRCATION)
|
||||
|
||||
# If file extension of the first script is '.pyw', force --windowed option.
|
||||
if is_win and os.path.splitext(scripts[0])[-1] == '.pyw':
|
||||
console = False
|
||||
|
||||
# If script paths are relative, make them relative to the directory containing .spec file.
|
||||
scripts = [make_path_spec_relative(x, specpath) for x in scripts]
|
||||
# With absolute paths replace prefix with variable HOMEPATH.
|
||||
scripts = list(map(Path, scripts))
|
||||
|
||||
# Translate the default of ``debug=None`` to an empty list.
|
||||
if debug is None:
|
||||
debug = []
|
||||
# Translate the ``all`` option.
|
||||
if DEBUG_ALL_CHOICE[0] in debug:
|
||||
debug = DEBUG_ARGUMENT_CHOICES
|
||||
|
||||
# Create preamble (for collect_*() calls)
|
||||
preamble = Preamble(
|
||||
datas, binaries, hiddenimports, collect_data, collect_binaries, collect_submodules, collect_all, copy_metadata,
|
||||
recursive_copy_metadata
|
||||
)
|
||||
|
||||
if splash:
|
||||
splash_options = ""
|
||||
if splash_center:
|
||||
splash_options = f"\n center={splash_center!r},"
|
||||
splash_init = splashtmpl % {
|
||||
'splash_image': splash,
|
||||
'splash_options': splash_options,
|
||||
}
|
||||
splash_binaries = "\n splash.binaries,"
|
||||
splash_target = "\n splash,"
|
||||
else:
|
||||
splash_init = splash_binaries = splash_target = ""
|
||||
|
||||
# Infer byte-code optimization level.
|
||||
opt_level = sum([opt == 'O' for opt in python_options])
|
||||
if opt_level > 2:
|
||||
logger.warning(
|
||||
"The switch '--python-option O' has been specified %d times - it should be specified at most twice!",
|
||||
opt_level,
|
||||
)
|
||||
opt_level = 2
|
||||
|
||||
if optimize is None:
|
||||
if opt_level == 0:
|
||||
# Infer from running python process
|
||||
optimize = sys.flags.optimize
|
||||
else:
|
||||
# Infer from `--python-option O` switch(es).
|
||||
optimize = opt_level
|
||||
elif optimize != opt_level and opt_level != 0:
|
||||
logger.warning(
|
||||
"Mismatch between optimization level passed via --optimize switch (%d) and number of '--python-option O' "
|
||||
"switches (%d)!",
|
||||
optimize,
|
||||
opt_level,
|
||||
)
|
||||
|
||||
if optimize >= 0:
|
||||
# Ensure OPTIONs passed to bootloader match the optimization settings.
|
||||
python_options += max(0, optimize - opt_level) * ['O']
|
||||
|
||||
# Create OPTIONs array
|
||||
if 'imports' in debug and 'v' not in python_options:
|
||||
python_options.append('v')
|
||||
python_options_array = [(opt, None, 'OPTION') for opt in python_options]
|
||||
|
||||
d = {
|
||||
'scripts': scripts,
|
||||
'pathex': pathex or [],
|
||||
'binaries': preamble.binaries,
|
||||
'datas': preamble.datas,
|
||||
'hiddenimports': preamble.hiddenimports,
|
||||
'preamble': preamble.content,
|
||||
'name': name,
|
||||
'noarchive': 'noarchive' in debug,
|
||||
'optimize': optimize,
|
||||
'options': python_options_array,
|
||||
'debug_bootloader': 'bootloader' in debug,
|
||||
'bootloader_ignore_signals': bootloader_ignore_signals,
|
||||
'strip': strip,
|
||||
'upx': not noupx,
|
||||
'upx_exclude': upx_exclude,
|
||||
'runtime_tmpdir': runtime_tmpdir,
|
||||
'exe_options': exe_options,
|
||||
# Directory with additional custom import hooks.
|
||||
'hookspath': hookspath,
|
||||
# List with custom runtime hook files.
|
||||
'runtime_hooks': runtime_hooks or [],
|
||||
# List of modules/packages to ignore.
|
||||
'excludes': excludes or [],
|
||||
# only Windows and macOS distinguish windowed and console apps
|
||||
'console': console,
|
||||
'disable_windowed_traceback': disable_windowed_traceback,
|
||||
# Icon filename. Only macOS uses this item.
|
||||
'icon': icon_file,
|
||||
# .app bundle identifier. Only macOS uses this item.
|
||||
'bundle_identifier': bundle_identifier,
|
||||
# argv emulation (macOS only)
|
||||
'argv_emulation': argv_emulation,
|
||||
# Target architecture (macOS only)
|
||||
'target_arch': target_arch,
|
||||
# Code signing identity (macOS only)
|
||||
'codesign_identity': codesign_identity,
|
||||
# Entitlements file (macOS only)
|
||||
'entitlements_file': entitlements_file,
|
||||
# splash screen
|
||||
'splash_init': splash_init,
|
||||
'splash_target': splash_target,
|
||||
'splash_binaries': splash_binaries,
|
||||
}
|
||||
|
||||
# Write down .spec file to filesystem.
|
||||
specfnm = os.path.join(specpath, name + '.spec')
|
||||
with open(specfnm, 'w', encoding='utf-8') as specfile:
|
||||
if onefile:
|
||||
specfile.write(onefiletmplt % d)
|
||||
# For macOS create .app bundle.
|
||||
if is_darwin and not console:
|
||||
specfile.write(bundleexetmplt % d)
|
||||
else:
|
||||
specfile.write(onedirtmplt % d)
|
||||
# For macOS create .app bundle.
|
||||
if is_darwin and not console:
|
||||
specfile.write(bundletmplt % d)
|
||||
|
||||
return specfnm
|
||||
775
venv/lib/python3.12/site-packages/PyInstaller/building/osx.py
Normal file
775
venv/lib/python3.12/site-packages/PyInstaller/building/osx.py
Normal file
@@ -0,0 +1,775 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import plistlib
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from PyInstaller import log as logging
|
||||
from PyInstaller.building.api import COLLECT, EXE
|
||||
from PyInstaller.building.datastruct import Target, logger, normalize_toc
|
||||
from PyInstaller.building.utils import _check_path_overlap, _rmtree, process_collected_binary
|
||||
from PyInstaller.compat import is_darwin, strict_collect_mode
|
||||
from PyInstaller.building.icon import normalize_icon_type
|
||||
import PyInstaller.utils.misc as miscutils
|
||||
|
||||
if is_darwin:
|
||||
import PyInstaller.utils.osx as osxutils
|
||||
|
||||
# Character sequence used to replace dot (`.`) in names of directories that are created in `Contents/MacOS` or
|
||||
# `Contents/Frameworks`, where only .framework bundle directories are allowed to have dot in name.
|
||||
DOT_REPLACEMENT = '__dot__'
|
||||
|
||||
WINDOWED_ONEFILE_DEPRCATION = (
|
||||
"Onefile mode in combination with macOS .app bundles (windowed mode) don't make sense (a .app bundle can not be a "
|
||||
"single file) and clashes with macOS's security. Please migrate to onedir mode. This will become an error "
|
||||
"in v7.0."
|
||||
)
|
||||
|
||||
|
||||
class BUNDLE(Target):
|
||||
def __init__(self, *args, **kwargs):
|
||||
from PyInstaller.config import CONF
|
||||
|
||||
for item in args:
|
||||
if isinstance(item, EXE) and not item.exclude_binaries:
|
||||
logger.log(logging.DEPRECATION, WINDOWED_ONEFILE_DEPRCATION)
|
||||
|
||||
# BUNDLE only has a sense under macOS, it is a noop on other platforms.
|
||||
if not is_darwin:
|
||||
return
|
||||
|
||||
# Get a path to a .icns icon for the app bundle.
|
||||
self.icon = kwargs.get('icon')
|
||||
if not self.icon:
|
||||
# --icon not specified; use the default in the pyinstaller folder
|
||||
self.icon = os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)), 'bootloader', 'images', 'icon-windowed.icns'
|
||||
)
|
||||
else:
|
||||
# User gave an --icon=path. If it is relative, make it relative to the spec file location.
|
||||
if not os.path.isabs(self.icon):
|
||||
self.icon = os.path.join(CONF['specpath'], self.icon)
|
||||
|
||||
super().__init__()
|
||||
|
||||
# .app bundle is created in DISTPATH.
|
||||
self.name = kwargs.get('name', None)
|
||||
base_name = os.path.basename(self.name)
|
||||
self.name = os.path.join(CONF['distpath'], base_name)
|
||||
|
||||
self.appname = os.path.splitext(base_name)[0]
|
||||
# Ensure version is a string, even if user accidentally passed an int or a float.
|
||||
# Having a `CFBundleShortVersionString` entry of non-string type in `Info.plist` causes the .app bundle to
|
||||
# crash at start (#4466).
|
||||
self.version = str(kwargs.get("version", "0.0.0"))
|
||||
self.toc = []
|
||||
self.strip = False
|
||||
self.upx = False
|
||||
self.console = True
|
||||
self.target_arch = None
|
||||
self.codesign_identity = None
|
||||
self.entitlements_file = None
|
||||
|
||||
# .app bundle identifier for Code Signing
|
||||
self.bundle_identifier = kwargs.get('bundle_identifier')
|
||||
if not self.bundle_identifier:
|
||||
# Fallback to appname.
|
||||
self.bundle_identifier = self.appname
|
||||
|
||||
self.info_plist = kwargs.get('info_plist', None)
|
||||
|
||||
for arg in args:
|
||||
# Valid arguments: EXE object, COLLECT object, and TOC-like iterables
|
||||
if isinstance(arg, EXE):
|
||||
# Add EXE as an entry to the TOC, and merge its dependencies TOC
|
||||
self.toc.append((os.path.basename(arg.name), arg.name, 'EXECUTABLE'))
|
||||
self.toc.extend(arg.dependencies)
|
||||
# Inherit settings
|
||||
self.strip = arg.strip
|
||||
self.upx = arg.upx
|
||||
self.upx_exclude = arg.upx_exclude
|
||||
self.console = arg.console
|
||||
self.target_arch = arg.target_arch
|
||||
self.codesign_identity = arg.codesign_identity
|
||||
self.entitlements_file = arg.entitlements_file
|
||||
elif isinstance(arg, COLLECT):
|
||||
# Merge the TOC
|
||||
self.toc.extend(arg.toc)
|
||||
# Inherit settings
|
||||
self.strip = arg.strip_binaries
|
||||
self.upx = arg.upx_binaries
|
||||
self.upx_exclude = arg.upx_exclude
|
||||
self.console = arg.console
|
||||
self.target_arch = arg.target_arch
|
||||
self.codesign_identity = arg.codesign_identity
|
||||
self.entitlements_file = arg.entitlements_file
|
||||
elif miscutils.is_iterable(arg):
|
||||
# TOC-like iterable
|
||||
self.toc.extend(arg)
|
||||
else:
|
||||
raise TypeError(f"Invalid argument type for BUNDLE: {type(arg)!r}")
|
||||
|
||||
# Infer the executable name from the first EXECUTABLE entry in the TOC; it might have come from the COLLECT
|
||||
# (as opposed to the stand-alone EXE).
|
||||
for dest_name, src_name, typecode in self.toc:
|
||||
if typecode == "EXECUTABLE":
|
||||
self.exename = src_name
|
||||
break
|
||||
else:
|
||||
raise ValueError("No EXECUTABLE entry found in the TOC!")
|
||||
|
||||
# Normalize TOC
|
||||
self.toc = normalize_toc(self.toc)
|
||||
|
||||
# Alphabetically sort the TOC to ensure that order of processing is predictable and reproducible.
|
||||
self.toc.sort()
|
||||
|
||||
self.__postinit__()
|
||||
|
||||
_GUTS = (
|
||||
# BUNDLE always builds, just want the toc to be written out
|
||||
('toc', None),
|
||||
)
|
||||
|
||||
def _check_guts(self, data, last_build):
|
||||
# BUNDLE always needs to be executed, in order to clean the output directory.
|
||||
return True
|
||||
|
||||
# Helper for determining whether the given file belongs to a .framework bundle or not. If it does, it returns
|
||||
# the path to the top-level .framework bundle directory; otherwise, returns None. In case of nested .framework
|
||||
# bundles, the path to the top-most .framework bundle directory is returned.
|
||||
@staticmethod
|
||||
def _is_framework_file(dest_path):
|
||||
# NOTE: reverse the parents list because we are looking for the top-most .framework bundle directory!
|
||||
for parent in reversed(dest_path.parents):
|
||||
if parent.name.endswith('.framework'):
|
||||
return parent
|
||||
return None
|
||||
|
||||
# Helper that computes relative cross-link path between link's location and target, assuming they are both
|
||||
# rooted in the `Contents` directory of a macOS .app bundle.
|
||||
@staticmethod
|
||||
def _compute_relative_crosslink(crosslink_location, crosslink_target):
|
||||
# We could take symlink_location and symlink_target as they are (relative to parent of the `Contents`
|
||||
# directory), but that would introduce an unnecessary `../Contents` part. So instead, we take both paths
|
||||
# relative to the `Contents` directory.
|
||||
return os.path.join(
|
||||
*['..' for level in pathlib.PurePath(crosslink_location).relative_to('Contents').parent.parts],
|
||||
pathlib.PurePath(crosslink_target).relative_to('Contents'),
|
||||
)
|
||||
|
||||
# This method takes the original (input) TOC and processes it into final TOC, based on which the `assemble` method
|
||||
# performs its file collection. The TOC processing here represents the core of our efforts to generate an .app
|
||||
# bundle that is compatible with Apple's code-signing requirements.
|
||||
#
|
||||
# For in-depth details on the code-signing, see Apple's `Technical Note TN2206: macOS Code Signing In Depth` at
|
||||
# https://developer.apple.com/library/archive/technotes/tn2206/_index.html
|
||||
#
|
||||
# The requirements, framed from PyInstaller's perspective, can be summarized as follows:
|
||||
#
|
||||
# 1. The `Contents/MacOS` directory is expected to contain only the program executable and (binary) code (= dylibs
|
||||
# and nested .framework bundles). Alternatively, the dylibs and .framework bundles can be also placed into
|
||||
# `Contents/Frameworks` directory (where same rules apply as for `Contents/MacOS`, so the remainder of this
|
||||
# text refers to the two inter-changeably, unless explicitly noted otherwise). The code in `Contents/MacOS`
|
||||
# is expected to be signed, and the `codesign` utility will recursively sign all found code when using `--deep`
|
||||
# option to sign the .app bundle.
|
||||
#
|
||||
# 2. All non-code files should be be placed in `Contents/Resources`, so they become sealed (data) resources;
|
||||
# i.e., their signature data is recorded in `Contents/_CodeSignature/CodeResources`. (As a side note,
|
||||
# it seems that signature information for data/resources in `Contents/Resources` is kept nder `file` key in
|
||||
# the `CodeResources` file, while the information for contents in `Contents/MacOS` is kept under `file2` key).
|
||||
#
|
||||
# 3. The directories in `Contents/MacOS` may not contain dots (`.`) in their names, except for the nested
|
||||
# .framework bundle directories. The directories in `Contents/Resources` have no such restrictions.
|
||||
#
|
||||
# 4. There may not be any content in the top level of a bundle. In other words, if a bundle has a `Contents`
|
||||
# or a `Versions` directory at its top level, there may be no other files or directories alongside them. The
|
||||
# sole exception is that alongside `Versions`, there may be symlinks to files and directories in
|
||||
# `Versions/Current`. This rule is important for nested .framework bundles that we collect from python packages.
|
||||
#
|
||||
# Next, let us consider the consequences of violating each of the above requirements:
|
||||
#
|
||||
# 1. Code signing machinery can directly store signature only in Mach-O binaries and nested .framework bundles; if
|
||||
# a data file is placed in `Contents/MacOS`, the signature is stored in the file's extended attributes. If the
|
||||
# extended attributes are lost, the program's signature will be broken. Many file transfer techniques (e.g., a
|
||||
# zip file) do not preserve extended attributes, nor are they preserved when uploading to the Mac App Store.
|
||||
#
|
||||
# 2. Putting code (a dylib or a .framework bundle) into `Contents/Resources` causes it to be treated as a resource;
|
||||
# the outer signature (i.e., of the whole .app bundle) does not know that this nested content is actually a code.
|
||||
# Consequently, signing the bundle with `codesign --deep` will NOT sign binaries placed in the
|
||||
# `Contents/Resources`, which may result in missing signatures when .app bundle is verified for notarization.
|
||||
# This might be worked around by signing each binary separately, and then signing the whole bundle (without the
|
||||
# `--deep` option), but requires the user to keep track of the offending binaries.
|
||||
#
|
||||
# 3. If a directory in `Contents/MacOS` contains a dot in the name, code-signing the bundle fails with
|
||||
# `bundle format unrecognized, invalid, or unsuitable` due to code signing machinery treating directory as a
|
||||
# nested .framework bundle directory.
|
||||
#
|
||||
# 4. If nested .framework bundle is malformed, the signing of the .app bundle might succeed, but subsequent
|
||||
# verification will fail, for example with `embedded framework contains modified or invalid version` (as observed
|
||||
# with .framework bundles shipped by contemporary PyQt/PySide PyPI wheels).
|
||||
#
|
||||
# The above requirements are unfortunately often at odds with the structure of python packages:
|
||||
#
|
||||
# * In general, python packages are mixed-content directories, where binaries and data files may be expected to
|
||||
# be found next to each other.
|
||||
#
|
||||
# For example, `opencv-python` provides a custom loader script that requires the package to be collected in the
|
||||
# source-only form by PyInstaller (i.e., the python modules and scripts collected as source .py files). At the
|
||||
# same time, it expects the .py loader script to be able to find the binary extension next to itself.
|
||||
#
|
||||
# Another example of mixed-mode directories are Qt QML components' sub-directories, which contain both the
|
||||
# component's plugin (a binary) and associated meta files (data files).
|
||||
#
|
||||
# * In python world, the directories often contain dots in their names.
|
||||
#
|
||||
# Dots are often used for private directories containing binaries that are shipped with a package. For example,
|
||||
# `numpy/.dylibs`, `scipy/.dylibs`, etc.
|
||||
#
|
||||
# Qt QML components may also contain a dot in their name; couple of examples from `PySide2` package:
|
||||
# `PySide2/Qt/qml/QtQuick.2`, `PySide2/Qt/qml/QtQuick/Controls.2`, `PySide2/Qt/qml/QtQuick/Particles.2`, etc.
|
||||
#
|
||||
# The packages' metadata directories also invariably contain dots in the name due to version (for example,
|
||||
# `numpy-1.24.3.dist-info`).
|
||||
#
|
||||
# In the light of all above, PyInstaller attempts to strictly place all files to their mandated location
|
||||
# (`Contents/MacOS` or `Contents/Frameworks` vs `Contents/Resources`). To preserve the illusion of mixed-content
|
||||
# directories, the content is cross-linked from one directory to the other. Specifically:
|
||||
#
|
||||
# * All entries with DATA typecode are assumed to be data files, and are always placed in corresponding directory
|
||||
# structure rooted in `Contents/Resources`.
|
||||
#
|
||||
# * All entries with BINARY or EXTENSION typecode are always placed in corresponding directory structure rooted in
|
||||
# `Contents/Frameworks`.
|
||||
#
|
||||
# * All entries with EXECUTABLE are placed in `Contents/MacOS` directory.
|
||||
#
|
||||
# * For the purposes of relocation, nested .framework bundles are treated as a single BINARY entity; i.e., the
|
||||
# whole .bundle directory is placed in corresponding directory structure rooted in `Contents/Frameworks` (even
|
||||
# though some of its contents, such as `Info.plist` file, are actually data files).
|
||||
#
|
||||
# * Top-level data files and binaries are always cross-linked to the other directory. For example, given a data file
|
||||
# `data_file.txt` that was collected into `Contents/Resources`, we create a symbolic link called
|
||||
# `Contents/MacOS/data_file.txt` that points to `../Resources/data_file.txt`.
|
||||
#
|
||||
# * The executable itself, while placed in `Contents/MacOS`, are cross-linked into both `Contents/Framworks` and
|
||||
# `Contents/Resources`.
|
||||
#
|
||||
# * The stand-alone PKG entries (used with onefile builds that side-load the PKG archive) are treated as data files
|
||||
# and collected into `Contents/Resources`, but cross-linked only into `Contents/MacOS` directory (because they
|
||||
# must appear to be next to the program executable). This is the only entry type that is cross-linked into the
|
||||
# `Contents/MacOS` directory and also the only data-like entry type that is not cross-linked into the
|
||||
# `Contents/Frameworks` directory.
|
||||
#
|
||||
# * For files in sub-directories, the cross-linking behavior depends on the type of directory:
|
||||
#
|
||||
# * A data-only directory is created in directory structure rooted in `Contents/Resources`, and cross-linked
|
||||
# into directory structure rooted in `Contents/Frameworks` at directory level (i.e., we link the whole
|
||||
# directory instead of individual files).
|
||||
#
|
||||
# This largely saves us from having to deal with dots in the names of collected metadata directories, which
|
||||
# are examples of data-only directories.
|
||||
#
|
||||
# * A binary-only directory is created in directory structure rooted in `Contents/Frameworks`, and cross-linked
|
||||
# into `Contents/Resources` at directory level.
|
||||
#
|
||||
# * A mixed-content directory is created in both directory structures. Files are placed into corresponding
|
||||
# directory structure based on their type, and cross-linked into other directory structure at file level.
|
||||
#
|
||||
# * This rule is applied recursively; for example, a data-only sub-directory in a mixed-content directory is
|
||||
# cross-linked at directory level, while adjacent binary and data files are cross-linked at file level.
|
||||
#
|
||||
# * To work around the issue with dots in the names of directories in `Contents/Frameworks` (applicable to
|
||||
# binary-only or mixed-content directories), such directories are created with modified name (the dot replaced
|
||||
# with a pre-defined pattern). Next to the modified directory, a symbolic link with original name is created,
|
||||
# pointing to the directory with modified name. With mixed-content directories, this modification is performed
|
||||
# only on the `Contents/Frameworks` side; the corresponding directory in `Contents/Resources` can be created
|
||||
# directly, without name modification and symbolic link.
|
||||
#
|
||||
# * If a symbolic link needs to be created in a mixed-content directory due to a SYMLINK entry from the original
|
||||
# TOC (i.e., a "collected" symlink originating from analysis, as opposed to the cross-linking mechanism described
|
||||
# above), the link is created in both directory structures, each pointing to the resource in its corresponding
|
||||
# directory structure (with one such resource being an actual file, and the other being a cross-link to the file).
|
||||
#
|
||||
# Final remarks:
|
||||
#
|
||||
# NOTE: the relocation mechanism is codified by tests in `tests/functional/test_macos_bundle_structure.py`.
|
||||
#
|
||||
# NOTE: by placing binaries and nested .framework entries into `Contents/Frameworks` instead of `Contents/MacOS`,
|
||||
# we have effectively relocated the `sys._MEIPASS` directory from the `Contents/MacOS` (= the parent directory of
|
||||
# the program executable) into `Contents/Frameworks`. This requires the PyInstaller's bootloader to detect that it
|
||||
# is running in the app-bundle mode (e.g., by checking if program executable's parent directory is `Contents/NacOS`)
|
||||
# and adjust the path accordingly.
|
||||
#
|
||||
# NOTE: the implemented relocation mechanism depends on the input TOC containing properly classified entries
|
||||
# w.r.t. BINARY vs DATA. So hooks and .spec files triggering collection of binaries as datas (and vice versa) will
|
||||
# result in incorrect placement of those files in the generated .app bundle. However, this is *not* the proper place
|
||||
# to address such issues; if necessary, automatic (re)classification should be added to analysis process, to ensure
|
||||
# that BUNDLE (as well as other build targets) receive correctly classified TOC.
|
||||
#
|
||||
# NOTE: similar to the previous note, the relocation mechanism is also not the proper place to enforce compliant
|
||||
# structure of the nested .framework bundles. Instead, this is handled by the analysis process, using the
|
||||
# `PyInstaller.utils.osx.collect_files_from_framework_bundles` helper function. So the input TOC that BUNDLE
|
||||
# receives should already contain entries that reconstruct compliant nested .framework bundles.
|
||||
def _process_bundle_toc(self, toc):
|
||||
bundle_toc = []
|
||||
|
||||
# Step 1: inspect the directory layout and classify the directories according to their contents.
|
||||
directory_types = dict()
|
||||
|
||||
_MIXED_DIR_TYPE = 'MIXED-DIR'
|
||||
_DATA_DIR_TYPE = 'DATA-DIR'
|
||||
_BINARY_DIR_TYPE = 'BINARY-DIR'
|
||||
_FRAMEWORK_DIR_TYPE = 'FRAMEWORK-DIR'
|
||||
|
||||
_UNKNOWN_DIR_TYPE = 'UNKNOWN-DIR' # used only in this step
|
||||
|
||||
_TOP_LEVEL_DIR = pathlib.PurePath('.')
|
||||
|
||||
for dest_name, src_name, typecode in toc:
|
||||
dest_path = pathlib.PurePath(dest_name)
|
||||
|
||||
framework_dir = self._is_framework_file(dest_path)
|
||||
if framework_dir:
|
||||
# Mark the framework directory as FRAMEWORK-DIR.
|
||||
directory_types[framework_dir] = _FRAMEWORK_DIR_TYPE
|
||||
# Treat the framework directory as BINARY file when classifying parent directories.
|
||||
typecode = 'BINARY'
|
||||
parent_dirs = framework_dir.parents
|
||||
else:
|
||||
parent_dirs = dest_path.parents
|
||||
# Treat BINARY and EXTENSION as BINARY to simplify further processing.
|
||||
if typecode == 'EXTENSION':
|
||||
typecode = 'BINARY'
|
||||
|
||||
# Directory type as per this entry's typecode. Symbolic links are "neutral". On the off chance that a
|
||||
# directory contains only symbolic link(s), we will override the type of "unknown" directories after this
|
||||
# loop finishes.
|
||||
if typecode == 'SYMLINK':
|
||||
entry_type = _UNKNOWN_DIR_TYPE
|
||||
elif typecode == 'BINARY':
|
||||
entry_type = _BINARY_DIR_TYPE
|
||||
else:
|
||||
entry_type = _DATA_DIR_TYPE
|
||||
|
||||
# (Re)classify parent directories
|
||||
for parent_dir in parent_dirs:
|
||||
# Skip the top-level `.` dir. This is also the only directory that can contain EXECUTABLE and PKG
|
||||
# entries, so we do not have to worry about them.
|
||||
if parent_dir == _TOP_LEVEL_DIR:
|
||||
continue
|
||||
|
||||
directory_type = directory_types.get(parent_dir, entry_type)
|
||||
|
||||
# If directory was previously marked as unknown, overwrite its type with the new one.
|
||||
if directory_type == _UNKNOWN_DIR_TYPE:
|
||||
directory_type = entry_type
|
||||
|
||||
# Update type into mixed-content, if necessary.
|
||||
if directory_type == _DATA_DIR_TYPE and entry_type == _BINARY_DIR_TYPE:
|
||||
directory_type = _MIXED_DIR_TYPE
|
||||
if directory_type == _BINARY_DIR_TYPE and entry_type == _DATA_DIR_TYPE:
|
||||
directory_type = _MIXED_DIR_TYPE
|
||||
|
||||
directory_types[parent_dir] = directory_type
|
||||
|
||||
# Reclassify all "unknown" directories into "data-only"; these are directories that contain only one or more
|
||||
# symbolic links.
|
||||
directory_types = {
|
||||
directory: directory_type if directory_type != _UNKNOWN_DIR_TYPE else _DATA_DIR_TYPE
|
||||
for directory, directory_type in directory_types.items()
|
||||
}
|
||||
|
||||
logger.debug("Directory classification: %r", directory_types)
|
||||
|
||||
# Step 2: process the obtained directory structure and create symlink entries for directories that need to be
|
||||
# cross-linked. Such directories are data-only and binary-only directories (and framework directories) that are
|
||||
# located either in the top-level directory (have no parent) or in a mixed-content directory.
|
||||
for directory_path, directory_type in directory_types.items():
|
||||
# Cross-linking at directory level applies only to data-only and binary-only directories (as well as
|
||||
# framework directories).
|
||||
if directory_type == _MIXED_DIR_TYPE:
|
||||
continue
|
||||
|
||||
# The parent needs to be either top-level directory or a mixed-content directory. Otherwise, the parent
|
||||
# (or one of its ancestors) will get cross-linked, and we do not need the link here.
|
||||
parent_dir = directory_path.parent
|
||||
requires_crosslink = parent_dir == _TOP_LEVEL_DIR or directory_types.get(parent_dir) == _MIXED_DIR_TYPE
|
||||
if not requires_crosslink:
|
||||
continue
|
||||
|
||||
logger.debug("Cross-linking directory %r of type %r", directory_path, directory_type)
|
||||
|
||||
# Data-only directories are created in `Contents/Resources`, needs to be cross-linked into `Contents/MacOS`.
|
||||
# Vice versa for binary-only or framework directories. The directory creation is handled implicitly, when we
|
||||
# create parent directory structure for collected files.
|
||||
if directory_type == _DATA_DIR_TYPE:
|
||||
symlink_src = os.path.join('Contents/Resources', directory_path)
|
||||
symlink_dest = os.path.join('Contents/Frameworks', directory_path)
|
||||
else:
|
||||
symlink_src = os.path.join('Contents/Frameworks', directory_path)
|
||||
symlink_dest = os.path.join('Contents/Resources', directory_path)
|
||||
symlink_ref = self._compute_relative_crosslink(symlink_dest, symlink_src)
|
||||
|
||||
bundle_toc.append((symlink_dest, symlink_ref, 'SYMLINK'))
|
||||
|
||||
# Step 3: first part of the work-around for directories that are located in `Contents/Frameworks` but contain a
|
||||
# dot in their name. As per `codesign` rules, the only directories in `Contents/Frameworks` that are allowed to
|
||||
# contain a dot in their name are .framework bundle directories. So we replace the dot with a custom character
|
||||
# sequence (stored in global `DOT_REPLACEMENT` variable), and create a symbolic with original name pointing to
|
||||
# the modified name. This is the best we can do with code-sign requirements vs. python community showing their
|
||||
# packages' dylibs into `.dylib` subdirectories, or Qt storing their Qml components in directories named
|
||||
# `QtQuick.2`, `QtQuick/Controls.2`, `QtQuick/Particles.2`, `QtQuick/Templates.2`, etc.
|
||||
#
|
||||
# In this step, we only prepare symlink entries that link the original directory name (with dot) to the modified
|
||||
# one (with dot replaced). The parent paths for collected files are modified in later step(s).
|
||||
for directory_path, directory_type in directory_types.items():
|
||||
# .framework bundle directories contain a dot in the name, but are allowed that.
|
||||
if directory_type == _FRAMEWORK_DIR_TYPE:
|
||||
continue
|
||||
|
||||
# Data-only directories are fully located in `Contents/Resources` and cross-linked to `Contents/Frameworks`
|
||||
# at directory level, so they are also allowed a dot in their name.
|
||||
if directory_type == _DATA_DIR_TYPE:
|
||||
continue
|
||||
|
||||
# Apply the work-around, if necessary...
|
||||
if '.' not in directory_path.name:
|
||||
continue
|
||||
|
||||
logger.debug(
|
||||
"Creating symlink to work around the dot in the name of directory %r (%s)...", str(directory_path),
|
||||
directory_type
|
||||
)
|
||||
|
||||
# Create a SYMLINK entry, but only for this level. In case of nested directories with dots in names, the
|
||||
# symlinks for ancestors will be created by corresponding loop iteration.
|
||||
bundle_toc.append((
|
||||
os.path.join('Contents/Frameworks', directory_path),
|
||||
directory_path.name.replace('.', DOT_REPLACEMENT),
|
||||
'SYMLINK',
|
||||
))
|
||||
|
||||
# Step 4: process the entries for collected files, and decide whether they should be placed into
|
||||
# `Contents/MacOS`, `Contents/Frameworks`, or `Contents/Resources`, and whether they should be cross-linked into
|
||||
# other directories.
|
||||
for orig_dest_name, src_name, typecode in toc:
|
||||
orig_dest_path = pathlib.PurePath(orig_dest_name)
|
||||
|
||||
# Special handling for EXECUTABLE and PKG entries
|
||||
if typecode == 'EXECUTABLE':
|
||||
# Place into `Contents/MacOS`, ...
|
||||
file_dest = os.path.join('Contents/MacOS', orig_dest_name)
|
||||
bundle_toc.append((file_dest, src_name, typecode))
|
||||
# ... and do nothing else. We explicitly avoid cross-linking the executable to `Contents/Frameworks` and
|
||||
# `Contents/Resources`, because it should be not necessary (the executable's location should be
|
||||
# discovered via `sys.executable`) and to prevent issues when executable name collides with name of a
|
||||
# package from which we collect either binaries or data files (or both); see #7314.
|
||||
continue
|
||||
elif typecode == 'PKG':
|
||||
# Place into `Contents/Resources` ...
|
||||
file_dest = os.path.join('Contents/Resources', orig_dest_name)
|
||||
bundle_toc.append((file_dest, src_name, typecode))
|
||||
# ... and cross-link only into `Contents/MacOS`.
|
||||
# This is used only in `onefile` mode, where there is actually no other content to distribute among the
|
||||
# `Contents/Resources` and `Contents/Frameworks` directories, so cross-linking into the latter makes
|
||||
# little sense.
|
||||
symlink_dest = os.path.join('Contents/MacOS', orig_dest_name)
|
||||
symlink_ref = self._compute_relative_crosslink(symlink_dest, file_dest)
|
||||
bundle_toc.append((symlink_dest, symlink_ref, 'SYMLINK'))
|
||||
continue
|
||||
|
||||
# Standard data vs binary processing...
|
||||
|
||||
# Determine file location based on its type.
|
||||
if self._is_framework_file(orig_dest_path):
|
||||
# File from a framework bundle; put into `Contents/Frameworks`, but never cross-link the file itself.
|
||||
# The whole .framework bundle directory will be linked as necessary by the directory cross-linking
|
||||
# mechanism.
|
||||
file_base_dir = 'Contents/Frameworks'
|
||||
crosslink_base_dir = None
|
||||
elif typecode == 'SYMLINK':
|
||||
# Symbolic links
|
||||
parent_dir = orig_dest_path.parent
|
||||
if parent_dir == _TOP_LEVEL_DIR or directory_types.get(parent_dir) == _MIXED_DIR_TYPE:
|
||||
# Symbolic links that need to be cross-linked (because they are located in top-level directory or
|
||||
# in a mixed-content directory) are instead created in both locations, and point to the (relative)
|
||||
# resource in the same directory; so one of the targets will likely be a file, and the other will
|
||||
# be a symlink due to cross-linking.
|
||||
bundle_toc.append((os.path.join('Contents/Frameworks', orig_dest_name), src_name, typecode))
|
||||
bundle_toc.append((os.path.join('Contents/Resources', orig_dest_name), src_name, typecode))
|
||||
continue
|
||||
elif directory_types.get(parent_dir) == _DATA_DIR_TYPE:
|
||||
# Symbolic link in a data-only directory; relocate to 'Contents/Resources' and do NOT cross-link
|
||||
# (since the whole directory will be cross-linked).
|
||||
file_base_dir = 'Contents/Resources'
|
||||
crosslink_base_dir = None
|
||||
else:
|
||||
# Symbolic link in a binary-only directory; similar to data-only directory, except we relocate to
|
||||
# 'Contents/Frameworks'.
|
||||
file_base_dir = 'Contents/Frameworks'
|
||||
crosslink_base_dir = None
|
||||
elif typecode == 'DATA':
|
||||
# Data file; relocate to `Contents/Resources` and cross-link it back into `Contents/Frameworks`.
|
||||
file_base_dir = 'Contents/Resources'
|
||||
crosslink_base_dir = 'Contents/Frameworks'
|
||||
else:
|
||||
# Binary; put into `Contents/Frameworks` and cross-link it into `Contents/Resources`.
|
||||
file_base_dir = 'Contents/Frameworks'
|
||||
crosslink_base_dir = 'Contents/Resources'
|
||||
|
||||
# Determine if we need to cross-link the file. We need to do this for top-level files (the ones without
|
||||
# parent directories), and for files whose parent directories are mixed-content directories.
|
||||
requires_crosslink = False
|
||||
if crosslink_base_dir is not None:
|
||||
parent_dir = orig_dest_path.parent
|
||||
requires_crosslink = parent_dir == _TOP_LEVEL_DIR or directory_types.get(parent_dir) == _MIXED_DIR_TYPE
|
||||
|
||||
# The file itself.
|
||||
file_dest = os.path.join(file_base_dir, orig_dest_name)
|
||||
bundle_toc.append((file_dest, src_name, typecode))
|
||||
|
||||
# Symlink for cross-linking
|
||||
if requires_crosslink:
|
||||
symlink_dest = os.path.join(crosslink_base_dir, orig_dest_name)
|
||||
symlink_ref = self._compute_relative_crosslink(symlink_dest, file_dest)
|
||||
bundle_toc.append((symlink_dest, symlink_ref, 'SYMLINK'))
|
||||
|
||||
# Step 5: sanitize all destination paths in the new TOC, to ensure that paths that are rooted in
|
||||
# `Contents/Frameworks` do not contain directories with dots in their names. Doing this as a post-processing
|
||||
# step keeps code simple and clean and ensures that this step is applied to files, symlinks that originate from
|
||||
# cross-linking files, and symlinks that originate from cross-linking directories. This in turn ensures that
|
||||
# all directory hierarchies created during the actual file collection have sanitized names, and that collection
|
||||
# outcome does not depend on the order of entries in the TOC.
|
||||
sanitized_toc = []
|
||||
for dest_name, src_name, typecode in bundle_toc:
|
||||
dest_path = pathlib.PurePath(dest_name)
|
||||
|
||||
# Paths rooted in Contents/Resources do not require sanitizing.
|
||||
if dest_path.parts[0] == 'Contents' and dest_path.parts[1] == 'Resources':
|
||||
sanitized_toc.append((dest_name, src_name, typecode))
|
||||
continue
|
||||
|
||||
# Special handling for files from .framework bundle directories; sanitize only parent path of the .framework
|
||||
# directory.
|
||||
framework_path = self._is_framework_file(dest_path)
|
||||
if framework_path:
|
||||
parent_path = framework_path.parent
|
||||
remaining_path = dest_path.relative_to(parent_path)
|
||||
else:
|
||||
parent_path = dest_path.parent
|
||||
remaining_path = dest_path.name
|
||||
|
||||
sanitized_dest_path = pathlib.PurePath(
|
||||
*parent_path.parts[:2], # Contents/Frameworks
|
||||
*[part.replace('.', DOT_REPLACEMENT) for part in parent_path.parts[2:]],
|
||||
remaining_path,
|
||||
)
|
||||
sanitized_dest_name = str(sanitized_dest_path)
|
||||
|
||||
if sanitized_dest_path != dest_path:
|
||||
logger.debug("Sanitizing dest path: %r -> %r", dest_name, sanitized_dest_name)
|
||||
|
||||
sanitized_toc.append((sanitized_dest_name, src_name, typecode))
|
||||
|
||||
bundle_toc = sanitized_toc
|
||||
|
||||
# Normalize and sort the TOC for easier inspection
|
||||
bundle_toc = sorted(normalize_toc(bundle_toc))
|
||||
|
||||
return bundle_toc
|
||||
|
||||
def assemble(self):
|
||||
from PyInstaller.config import CONF
|
||||
|
||||
if _check_path_overlap(self.name) and os.path.isdir(self.name):
|
||||
_rmtree(self.name)
|
||||
|
||||
logger.info("Building BUNDLE %s", self.tocbasename)
|
||||
|
||||
# Create a minimal Mac bundle structure.
|
||||
os.makedirs(os.path.join(self.name, "Contents", "MacOS"))
|
||||
os.makedirs(os.path.join(self.name, "Contents", "Resources"))
|
||||
os.makedirs(os.path.join(self.name, "Contents", "Frameworks"))
|
||||
|
||||
# Makes sure the icon exists and attempts to convert to the proper format if applicable
|
||||
self.icon = normalize_icon_type(self.icon, ("icns",), "icns", CONF["workpath"])
|
||||
|
||||
# Ensure icon path is absolute
|
||||
self.icon = os.path.abspath(self.icon)
|
||||
|
||||
# Copy icns icon to Resources directory.
|
||||
shutil.copyfile(self.icon, os.path.join(self.name, 'Contents', 'Resources', os.path.basename(self.icon)))
|
||||
|
||||
# Key/values for a minimal Info.plist file
|
||||
info_plist_dict = {
|
||||
"CFBundleDisplayName": self.appname,
|
||||
"CFBundleName": self.appname,
|
||||
|
||||
# Required by 'codesign' utility.
|
||||
# The value for CFBundleIdentifier is used as the default unique name of your program for Code Signing
|
||||
# purposes. It even identifies the APP for access to restricted macOS areas like Keychain.
|
||||
#
|
||||
# The identifier used for signing must be globally unique. The usual form for this identifier is a
|
||||
# hierarchical name in reverse DNS notation, starting with the toplevel domain, followed by the company
|
||||
# name, followed by the department within the company, and ending with the product name. Usually in the
|
||||
# form: com.mycompany.department.appname
|
||||
# CLI option --osx-bundle-identifier sets this value.
|
||||
"CFBundleIdentifier": self.bundle_identifier,
|
||||
"CFBundleExecutable": os.path.basename(self.exename),
|
||||
"CFBundleIconFile": os.path.basename(self.icon),
|
||||
"CFBundleInfoDictionaryVersion": "6.0",
|
||||
"CFBundlePackageType": "APPL",
|
||||
"CFBundleShortVersionString": self.version,
|
||||
}
|
||||
|
||||
# Set some default values. But they still can be overwritten by the user.
|
||||
if self.console:
|
||||
# Setting EXE console=True implies LSBackgroundOnly=True.
|
||||
info_plist_dict['LSBackgroundOnly'] = True
|
||||
else:
|
||||
# Let's use high resolution by default.
|
||||
info_plist_dict['NSHighResolutionCapable'] = True
|
||||
|
||||
# Merge info_plist settings from spec file
|
||||
if isinstance(self.info_plist, dict) and self.info_plist:
|
||||
info_plist_dict.update(self.info_plist)
|
||||
|
||||
plist_filename = os.path.join(self.name, "Contents", "Info.plist")
|
||||
with open(plist_filename, "wb") as plist_fh:
|
||||
plistlib.dump(info_plist_dict, plist_fh)
|
||||
|
||||
# Pre-process the TOC into its final BUNDLE-compatible form.
|
||||
bundle_toc = self._process_bundle_toc(self.toc)
|
||||
|
||||
# Perform the actual collection.
|
||||
CONTENTS_FRAMEWORKS_PATH = pathlib.PurePath('Contents/Frameworks')
|
||||
for dest_name, src_name, typecode in bundle_toc:
|
||||
# Create parent directory structure, if necessary
|
||||
dest_path = os.path.join(self.name, dest_name) # Absolute destination path
|
||||
dest_dir = os.path.dirname(dest_path)
|
||||
try:
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
except FileExistsError:
|
||||
raise SystemExit(
|
||||
f"ERROR: Pyinstaller needs to create a directory at {dest_dir!r}, "
|
||||
"but there already exists a file at that path!"
|
||||
)
|
||||
# Copy extensions and binaries from cache. This ensures that these files undergo additional binary
|
||||
# processing - have paths to linked libraries rewritten (relative to `@rpath`) and have rpath set to the
|
||||
# top-level directory (relative to `@loader_path`, i.e., the file's location). The "top-level" directory
|
||||
# in this case corresponds to `Contents/MacOS` (where `sys._MEIPASS` also points), so we need to pass
|
||||
# the cache retrieval function the *original* destination path (which is without preceding
|
||||
# `Contents/MacOS`).
|
||||
if typecode in ('EXTENSION', 'BINARY'):
|
||||
orig_dest_name = str(pathlib.PurePath(dest_name).relative_to(CONTENTS_FRAMEWORKS_PATH))
|
||||
src_name = process_collected_binary(
|
||||
src_name,
|
||||
orig_dest_name,
|
||||
use_strip=self.strip,
|
||||
use_upx=self.upx,
|
||||
upx_exclude=self.upx_exclude,
|
||||
target_arch=self.target_arch,
|
||||
codesign_identity=self.codesign_identity,
|
||||
entitlements_file=self.entitlements_file,
|
||||
strict_arch_validation=(typecode == 'EXTENSION'),
|
||||
)
|
||||
if typecode == 'SYMLINK':
|
||||
os.symlink(src_name, dest_path) # Create link at dest_path, pointing at (relative) src_name
|
||||
else:
|
||||
# BUNDLE does not support MERGE-based multipackage
|
||||
assert typecode != 'DEPENDENCY', "MERGE DEPENDENCY entries are not supported in BUNDLE!"
|
||||
|
||||
# At this point, `src_name` should be a valid file.
|
||||
if not os.path.isfile(src_name):
|
||||
raise ValueError(f"Resource {src_name!r} is not a valid file!")
|
||||
# If strict collection mode is enabled, the destination should not exist yet.
|
||||
if strict_collect_mode and os.path.exists(dest_path):
|
||||
raise ValueError(
|
||||
f"Attempting to collect a duplicated file into BUNDLE: {dest_name} (type: {typecode})"
|
||||
)
|
||||
# Use `shutil.copyfile` to copy file with default permissions. We do not attempt to preserve original
|
||||
# permissions nor metadata, as they might be too restrictive and cause issues either during subsequent
|
||||
# re-build attempts or when trying to move the application bundle. For binaries (and data files with
|
||||
# executable bit set), we manually set the executable bits after copying the file.
|
||||
shutil.copyfile(src_name, dest_path)
|
||||
if (
|
||||
typecode in ('EXTENSION', 'BINARY', 'EXECUTABLE')
|
||||
or (typecode == 'DATA' and os.access(src_name, os.X_OK))
|
||||
):
|
||||
os.chmod(dest_path, 0o755)
|
||||
|
||||
# Sign the bundle
|
||||
logger.info('Signing the BUNDLE...')
|
||||
try:
|
||||
osxutils.sign_binary(self.name, self.codesign_identity, self.entitlements_file, deep=True)
|
||||
except Exception as e:
|
||||
# Display a warning or re-raise the error, depending on the environment-variable setting.
|
||||
if os.environ.get("PYINSTALLER_STRICT_BUNDLE_CODESIGN_ERROR", "0") == "0":
|
||||
logger.warning("Error while signing the bundle: %s", e)
|
||||
logger.warning("You will need to sign the bundle manually!")
|
||||
else:
|
||||
raise RuntimeError("Failed to codesign the bundle!") from e
|
||||
|
||||
logger.info("Building BUNDLE %s completed successfully.", self.tocbasename)
|
||||
|
||||
# Optionally verify bundle's signature. This is primarily intended for our CI.
|
||||
if os.environ.get("PYINSTALLER_VERIFY_BUNDLE_SIGNATURE", "0") != "0":
|
||||
logger.info("Verifying signature for BUNDLE %s...", self.name)
|
||||
self.verify_bundle_signature(self.name)
|
||||
logger.info("BUNDLE verification complete!")
|
||||
|
||||
@staticmethod
|
||||
def verify_bundle_signature(bundle_dir):
|
||||
# First, verify the bundle signature using codesign.
|
||||
cmd_args = ['/usr/bin/codesign', '--verify', '--all-architectures', '--deep', '--strict', bundle_dir]
|
||||
p = subprocess.run(cmd_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
|
||||
if p.returncode:
|
||||
raise SystemError(
|
||||
f"codesign command ({cmd_args}) failed with error code {p.returncode}!\noutput: {p.stdout}"
|
||||
)
|
||||
|
||||
# Ensure that code-signing information is *NOT* embedded in the files' extended attributes.
|
||||
#
|
||||
# This happens when files other than binaries are present in `Contents/MacOS` or `Contents/Frameworks`
|
||||
# directory; as the signature cannot be embedded within the file itself (contrary to binaries with
|
||||
# `LC_CODE_SIGNATURE` section in their header), it ends up stores in the file's extended attributes. However,
|
||||
# if such bundle is transferred using a method that does not support extended attributes (for example, a zip
|
||||
# file), the signatures on these files are lost, and the signature of the bundle as a whole becomes invalid.
|
||||
# This is the primary reason why we need to relocate non-binaries into `Contents/Resources` - the signatures
|
||||
# for files in that directory end up stored in `Contents/_CodeSignature/CodeResources` file.
|
||||
#
|
||||
# This check therefore aims to ensure that all files have been properly relocated to their corresponding
|
||||
# locations w.r.t. the code-signing requirements.
|
||||
|
||||
try:
|
||||
import xattr
|
||||
except ModuleNotFoundError:
|
||||
logger.info("xattr package not available; skipping verification of extended attributes!")
|
||||
return
|
||||
|
||||
CODESIGN_ATTRS = (
|
||||
"com.apple.cs.CodeDirectory",
|
||||
"com.apple.cs.CodeRequirements",
|
||||
"com.apple.cs.CodeRequirements-1",
|
||||
"com.apple.cs.CodeSignature",
|
||||
)
|
||||
|
||||
for entry in pathlib.Path(bundle_dir).rglob("*"):
|
||||
if not entry.is_file():
|
||||
continue
|
||||
|
||||
file_attrs = xattr.listxattr(entry)
|
||||
if any([codesign_attr in file_attrs for codesign_attr in CODESIGN_ATTRS]):
|
||||
raise ValueError(f"Code-sign attributes found in extended attributes of {str(entry)!r}!")
|
||||
513
venv/lib/python3.12/site-packages/PyInstaller/building/splash.py
Normal file
513
venv/lib/python3.12/site-packages/PyInstaller/building/splash.py
Normal file
@@ -0,0 +1,513 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
# -----------------------------------------------------------------------------
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
import pathlib
|
||||
|
||||
from PyInstaller import log as logging
|
||||
from PyInstaller.archive.writers import SplashWriter
|
||||
from PyInstaller.building import splash_templates
|
||||
from PyInstaller.building.datastruct import Target
|
||||
from PyInstaller.building.utils import _check_guts_eq, _check_guts_toc, misc
|
||||
from PyInstaller.compat import is_aix, is_darwin
|
||||
from PyInstaller.depend import bindepend
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Splash(Target):
|
||||
"""
|
||||
Bundles the required resources for the splash screen into a file, which will be included in the CArchive.
|
||||
|
||||
A Splash has two outputs, one is itself and one is stored in splash.binaries. Both need to be passed to other
|
||||
build targets in order to enable the splash screen.
|
||||
"""
|
||||
def __init__(self, image_file, binaries, datas, **kwargs):
|
||||
"""
|
||||
:param str image_file:
|
||||
A path-like object to the image to be used. Only the PNG file format is supported.
|
||||
|
||||
.. note:: If a different file format is supplied and PIL (Pillow) is installed, the file will be converted
|
||||
automatically.
|
||||
|
||||
.. note:: *Windows*: The color ``'magenta'`` / ``'#ff00ff'`` must not be used in the image or text, as it is
|
||||
used by splash screen to indicate transparent areas. Use a similar color (e.g., ``'#ff00fe'``) instead.
|
||||
|
||||
.. note:: If PIL (Pillow) is installed and the image is bigger than max_img_size, the image will be resized
|
||||
to fit into the specified area.
|
||||
:param list binaries:
|
||||
The TOC list of binaries the Analysis build target found. This TOC includes all extension modules and their
|
||||
binary dependencies. This is required to determine whether the user's program uses `tkinter`.
|
||||
:param list datas:
|
||||
The TOC list of data the Analysis build target found. This TOC includes all data-file dependencies of the
|
||||
modules. This is required to check if all splash screen requirements can be bundled.
|
||||
|
||||
:keyword text_pos:
|
||||
An optional two-integer tuple that represents the origin of the text on the splash screen image. The
|
||||
origin of the text is its lower left corner. A unit in the respective coordinate system is a pixel of the
|
||||
image, its origin lies in the top left corner of the image. This parameter also acts like a switch for
|
||||
the text feature. If omitted, no text will be displayed on the splash screen. This text will be used to
|
||||
show textual progress in onefile mode.
|
||||
:type text_pos: Tuple[int, int]
|
||||
:keyword text_size:
|
||||
The desired size of the font. If the size argument is a positive number, it is interpreted as a size in
|
||||
points. If size is a negative number, its absolute value is interpreted as a size in pixels. Default: ``12``
|
||||
:type text_size: int
|
||||
:keyword text_font:
|
||||
An optional name of a font for the text. This font must be installed on the user system, otherwise the
|
||||
system default font is used. If this parameter is omitted, the default font is also used.
|
||||
:keyword text_color:
|
||||
An optional color for the text. HTML color codes (``'#40e0d0'``) and color names (``'turquoise'``) are
|
||||
supported. Default: ``'black'``
|
||||
(Windows: the color ``'magenta'`` / ``'#ff00ff'`` is used to indicate transparency, and should not be used)
|
||||
:type text_color: str
|
||||
:keyword text_default:
|
||||
The default text which will be displayed before the extraction starts. Default: ``"Initializing"``
|
||||
:type text_default: str
|
||||
:keyword full_tk:
|
||||
By default Splash bundles only the necessary files for the splash screen (some tk components). This
|
||||
options enables adding full tk and making it a requirement, meaning all tk files will be unpacked before
|
||||
the splash screen can be started. This is useful during development of the splash screen script.
|
||||
Default: ``False``
|
||||
:type full_tk: bool
|
||||
:keyword minify_script:
|
||||
The splash screen is created by executing an Tcl/Tk script. This option enables minimizing the script,
|
||||
meaning removing all non essential parts from the script. Default: ``True``
|
||||
:keyword name:
|
||||
An optional alternative filename for the .res file. If not specified, a name is generated.
|
||||
:type name: str
|
||||
:keyword script_name:
|
||||
An optional alternative filename for the Tcl script, that will be generated. If not specified, a name is
|
||||
generated.
|
||||
:type script_name: str
|
||||
:keyword max_img_size:
|
||||
Maximum size of the splash screen image as a tuple. If the supplied image exceeds this limit, it will be
|
||||
resized to fit the maximum width (to keep the original aspect ratio). This option can be disabled by
|
||||
setting it to None. Default: ``(760, 480)``
|
||||
:type max_img_size: Tuple[int, int]
|
||||
:keyword always_on_top:
|
||||
Force the splashscreen to be always on top of other windows. If disabled, other windows (e.g., from other
|
||||
applications) can cover the splash screen by user bringing them to front. This might be useful for
|
||||
frozen applications with long startup times. Default: ``True``
|
||||
:type always_on_top: bool
|
||||
:keyword center:
|
||||
Splash screen centering mode: ``'default'``, ``'primary'``, ``'virtual'``, or ``'active'``. In the default
|
||||
mode, the splash screen script computes the position using screen dimensions obtained via Tk's ``winfo``
|
||||
command, which has platform-specific behavior in multi-monitor setups; on Windows, it seems to return the
|
||||
size of the primary monitor, while on other platforms, it seems to return the size of the whole virtual
|
||||
screen. In other modes, the bootloader attempts to query the target screen size (and position) using
|
||||
platform-specific low-level API, and supply the information to splash screen script; in ``primary`` mode,
|
||||
the size of primary screen is queried, and in ``virtual`` mode, the size of whole virtual screen is queried.
|
||||
The ``active`` mode is supported only on Windows; the bootloader attempts to obtain position of mouse cursor
|
||||
at the time when application is launched, and queries the size (and position) of the corresponding screen.
|
||||
If the required information cannot be obtained and exposed by the bootloader, the splash screen script
|
||||
falls back to the information provided by the ``winfo`` command. Default: ``'default'``
|
||||
:type center: str
|
||||
"""
|
||||
from PyInstaller.config import CONF
|
||||
from PyInstaller.utils.hooks.tcl_tk import tcltk_info
|
||||
|
||||
Target.__init__(self)
|
||||
|
||||
# Splash screen is not supported on macOS. It operates in a secondary thread and macOS disallows UI operations
|
||||
# in any thread other than main.
|
||||
if is_darwin:
|
||||
raise SystemExit("ERROR: Splash screen is not supported on macOS.")
|
||||
|
||||
# Ensure tkinter (and thus Tcl/Tk) is available.
|
||||
if not tcltk_info.available:
|
||||
raise SystemExit(
|
||||
"ERROR: Your platform does not support the splash screen feature, since tkinter is not installed. "
|
||||
"Please install tkinter and try again."
|
||||
)
|
||||
|
||||
# Check if the Tcl/Tk version is supported.
|
||||
logger.info("Verifying Tcl/Tk compatibility with splash screen requirements")
|
||||
self._check_tcl_tk_compatibility(tcltk_info)
|
||||
|
||||
# Make image path relative to .spec file
|
||||
if not os.path.isabs(image_file):
|
||||
image_file = os.path.join(CONF['specpath'], image_file)
|
||||
image_file = os.path.normpath(image_file)
|
||||
if not os.path.exists(image_file):
|
||||
raise ValueError("Image file '%s' not found" % image_file)
|
||||
|
||||
# Copy all arguments
|
||||
self.image_file = image_file
|
||||
self.full_tk = kwargs.get("full_tk", False)
|
||||
self.name = kwargs.get("name", None)
|
||||
self.script_name = kwargs.get("script_name", None)
|
||||
self.minify_script = kwargs.get("minify_script", True)
|
||||
self.max_img_size = kwargs.get("max_img_size", (760, 480))
|
||||
|
||||
self.center = kwargs.get("center", "default").lower()
|
||||
if self.center not in self._CENTER_MODES:
|
||||
valid_modes = list(self._CENTER_MODES.keys())
|
||||
raise ValueError(f"Invalid center mode {self.center!r}! Must be one of: {valid_modes!r}")
|
||||
|
||||
# text options
|
||||
self.text_pos = kwargs.get("text_pos", None)
|
||||
self.text_size = kwargs.get("text_size", 12)
|
||||
self.text_font = kwargs.get("text_font", "TkDefaultFont")
|
||||
self.text_color = kwargs.get("text_color", "black")
|
||||
self.text_default = kwargs.get("text_default", "Initializing")
|
||||
|
||||
# always-on-top behavior
|
||||
self.always_on_top = kwargs.get("always_on_top", True)
|
||||
|
||||
# Save the generated file separately so that it is not necessary to generate the data again and again
|
||||
root = os.path.splitext(self.tocfilename)[0]
|
||||
if self.name is None:
|
||||
self.name = root + '.res'
|
||||
if self.script_name is None:
|
||||
self.script_name = root + '_script.tcl'
|
||||
|
||||
# Internal variables
|
||||
# Store path to _tkinter extension module, so that guts check can detect if the path changed for some reason.
|
||||
self._tkinter_file = tcltk_info.tkinter_extension_file
|
||||
|
||||
# Calculated / analysed values
|
||||
self.uses_tkinter = self._uses_tkinter(self._tkinter_file, binaries)
|
||||
logger.debug("Program uses tkinter: %r", self.uses_tkinter)
|
||||
self.script = self.generate_script()
|
||||
self.tcl_lib = tcltk_info.tcl_shared_library # full path to shared library
|
||||
self.tk_lib = tcltk_info.tk_shared_library
|
||||
|
||||
assert self.tcl_lib is not None
|
||||
assert self.tk_lib is not None
|
||||
|
||||
logger.debug("Using Tcl shared library: %r", self.tcl_lib)
|
||||
logger.debug("Using Tk shared library: %r", self.tk_lib)
|
||||
|
||||
self.splash_requirements = set([
|
||||
# NOTE: the implicit assumption here is that Tcl and Tk shared library are collected into top-level
|
||||
# application directory, which, at tme moment, is true in practically all cases.
|
||||
os.path.basename(self.tcl_lib),
|
||||
os.path.basename(self.tk_lib),
|
||||
# The list of requirements below is based on the current implementation of splash screen script. If you want
|
||||
# to extend the splash screen functionality and run into Tcl/Tk errors, chances are that additional Tk
|
||||
# components need to be added here.
|
||||
#
|
||||
# NOTE: these paths use the *destination* layout for Tcl/Tk scripts, which uses unversioned tcl and tk
|
||||
# directories (see `PyInstaller.utils.hooks.tcl_tk.collect_tcl_tk_files`).
|
||||
os.path.join(tcltk_info.TCL_ROOTNAME, "init.tcl"),
|
||||
# Core Tk
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "license.terms"),
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "text.tcl"),
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "tk.tcl"),
|
||||
# Used for customizable font
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "ttk", "ttk.tcl"),
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "ttk", "fonts.tcl"),
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "ttk", "cursors.tcl"),
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "ttk", "utils.tcl"),
|
||||
])
|
||||
|
||||
if tcltk_info.tk_version >= (9, 0):
|
||||
self.splash_requirements.update([
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "scaling.tcl"),
|
||||
os.path.join(tcltk_info.TK_ROOTNAME, "tclIndex"), # required for auto-load of scaling.tcl
|
||||
])
|
||||
|
||||
logger.info("Collect Tcl/Tk data files for the splash screen")
|
||||
tcltk_tree = tcltk_info.data_files # 3-element tuple TOC
|
||||
if self.full_tk:
|
||||
# The user wants a full copy of Tk, so make all Tk files a requirement.
|
||||
self.splash_requirements.update(entry[0] for entry in tcltk_tree)
|
||||
|
||||
# Scan for binary dependencies of the Tcl/Tk shared libraries, and add them to `binaries` TOC list (which
|
||||
# should really be called `dependencies` as it is not limited to binaries. But it is too late now, and
|
||||
# existing spec files depend on this naming). We specify these binary dependencies (which include the
|
||||
# Tcl and Tk shared libraries themselves) even if the user's program uses tkinter and they would be collected
|
||||
# anyway; let the collection mechanism deal with potential duplicates.
|
||||
tcltk_libs = [(os.path.basename(src_name), src_name, 'BINARY') for src_name in (self.tcl_lib, self.tk_lib)]
|
||||
self.binaries = bindepend.binary_dependency_analysis(tcltk_libs)
|
||||
|
||||
# Put all shared library dependencies in `splash_requirements`, so they are made available in onefile mode.
|
||||
self.splash_requirements.update(entry[0] for entry in self.binaries)
|
||||
|
||||
# If the user's program does not use tkinter, add resources from Tcl/Tk tree to the dependencies list.
|
||||
# Do so only for the resources that are part of splash requirements.
|
||||
if not self.uses_tkinter:
|
||||
self.binaries.extend(entry for entry in tcltk_tree if entry[0] in self.splash_requirements)
|
||||
|
||||
# Check if all requirements were found.
|
||||
collected_files = set(entry[0] for entry in (binaries + datas + self.binaries))
|
||||
|
||||
def _filter_requirement(filename):
|
||||
if filename not in collected_files:
|
||||
# Item is not bundled, so warn the user about it. This actually may happen on some tkinter installations
|
||||
# that are missing the license.terms file - as this file has no effect on operation of splash screen,
|
||||
# suppress the warning for it.
|
||||
if os.path.basename(filename) == 'license.terms':
|
||||
return False
|
||||
|
||||
logger.warning(
|
||||
"The local Tcl/Tk installation is missing the file %s. The behavior of the splash screen is "
|
||||
"therefore undefined and may be unsupported.", filename
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
# Remove all files which were not found.
|
||||
self.splash_requirements = set(filter(_filter_requirement, self.splash_requirements))
|
||||
|
||||
logger.debug("Splash Requirements: %s", self.splash_requirements)
|
||||
|
||||
# On AIX, the Tcl and Tk shared libraries might in fact be ar archives with shared object inside it, and need to
|
||||
# be `dlopen`'ed with full name (for example, `libtcl.a(libtcl.so.8.6)` and `libtk.a(libtk.so.8.6)`. So if the
|
||||
# library's suffix is .a, adjust the name accordingly, assuming fixed format for the shared object name.
|
||||
# Adjust the names at the end of this method, because preceding steps use `self.tcl_lib` and `self.tk_lib` for
|
||||
# filesystem-based operations and need the original filenames.
|
||||
if is_aix:
|
||||
_, ext = os.path.splitext(self.tcl_lib)
|
||||
if ext == '.a':
|
||||
tcl_major, tcl_minor = tcltk_info.tcl_version
|
||||
self.tcl_lib += f"(libtcl.so.{tcl_major}.{tcl_minor})"
|
||||
_, ext = os.path.splitext(self.tk_lib)
|
||||
if ext == '.a':
|
||||
tk_major, tk_minor = tcltk_info.tk_version
|
||||
self.tk_lib += f"(libtk.so.{tk_major}.{tk_minor})"
|
||||
|
||||
self.__postinit__()
|
||||
|
||||
_CENTER_MODES = {
|
||||
'default': SplashWriter._SPLASH_CENTER_DEFAULT,
|
||||
'primary': SplashWriter._SPLASH_CENTER_PRIMARY_SCREEN,
|
||||
'virtual': SplashWriter._SPLASH_CENTER_VIRTUAL_SCREEN,
|
||||
'active': SplashWriter._SPLASH_CENTER_ACTIVE_SCREEN,
|
||||
}
|
||||
|
||||
_GUTS = (
|
||||
# input parameters
|
||||
('image_file', _check_guts_eq),
|
||||
('name', _check_guts_eq),
|
||||
('script_name', _check_guts_eq),
|
||||
('text_pos', _check_guts_eq),
|
||||
('text_size', _check_guts_eq),
|
||||
('text_font', _check_guts_eq),
|
||||
('text_color', _check_guts_eq),
|
||||
('text_default', _check_guts_eq),
|
||||
('always_on_top', _check_guts_eq),
|
||||
('full_tk', _check_guts_eq),
|
||||
('minify_script', _check_guts_eq),
|
||||
('max_img_size', _check_guts_eq),
|
||||
('center', _check_guts_eq),
|
||||
# calculated/analysed values
|
||||
('uses_tkinter', _check_guts_eq),
|
||||
('script', _check_guts_eq),
|
||||
('tcl_lib', _check_guts_eq),
|
||||
('tk_lib', _check_guts_eq),
|
||||
('splash_requirements', _check_guts_eq),
|
||||
('binaries', _check_guts_toc),
|
||||
# internal value
|
||||
# Check if the tkinter installation changed. This is theoretically possible if someone uses two different python
|
||||
# installations of the same version.
|
||||
('_tkinter_file', _check_guts_eq),
|
||||
)
|
||||
|
||||
def _check_guts(self, data, last_build):
|
||||
if Target._check_guts(self, data, last_build):
|
||||
return True
|
||||
|
||||
# Check if the image has been modified.
|
||||
if misc.mtime(self.image_file) > last_build:
|
||||
logger.info("Building %s because file %s changed", self.tocbasename, self.image_file)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def assemble(self):
|
||||
logger.info("Building Splash %s", self.name)
|
||||
|
||||
# Check if PIL/pillow is available.
|
||||
try:
|
||||
from PIL import Image as PILImage
|
||||
except ImportError:
|
||||
PILImage = None
|
||||
|
||||
# Required to pass tcltk_info.TK_ROOTNAME to SplashWriter
|
||||
from PyInstaller.utils.hooks.tcl_tk import tcltk_info
|
||||
|
||||
# Function to resize a given image to fit into the area defined by max_img_size.
|
||||
def _resize_image(_image, _orig_size):
|
||||
if PILImage:
|
||||
_w, _h = _orig_size
|
||||
_ratio_w = self.max_img_size[0] / _w
|
||||
if _ratio_w < 1:
|
||||
# Image width exceeds limit
|
||||
_h = int(_h * _ratio_w)
|
||||
_w = self.max_img_size[0]
|
||||
|
||||
_ratio_h = self.max_img_size[1] / _h
|
||||
if _ratio_h < 1:
|
||||
# Image height exceeds limit
|
||||
_w = int(_w * _ratio_h)
|
||||
_h = self.max_img_size[1]
|
||||
|
||||
# If a file is given it will be open
|
||||
if isinstance(_image, PILImage.Image):
|
||||
_img = _image
|
||||
else:
|
||||
_img = PILImage.open(_image)
|
||||
_img_resized = _img.resize((_w, _h))
|
||||
|
||||
# Save image into a stream
|
||||
_image_stream = io.BytesIO()
|
||||
_img_resized.save(_image_stream, format='PNG')
|
||||
_img.close()
|
||||
_img_resized.close()
|
||||
_image_data = _image_stream.getvalue()
|
||||
logger.info("Resized image %s from dimensions %r to (%d, %d)", self.image_file, _orig_size, _w, _h)
|
||||
return _image_data
|
||||
else:
|
||||
raise ValueError(
|
||||
"The splash image dimensions (w: %d, h: %d) exceed max_img_size (w: %d, h:%d), but the image "
|
||||
"cannot be resized due to missing PIL.Image! Either install the Pillow package, adjust the "
|
||||
"max_img_size, or use an image of compatible dimensions." %
|
||||
(_orig_size[0], _orig_size[1], self.max_img_size[0], self.max_img_size[1])
|
||||
)
|
||||
|
||||
# Open image file
|
||||
image_file = open(self.image_file, 'rb')
|
||||
|
||||
# Check header of the file to identify it
|
||||
if image_file.read(8) == b'\x89PNG\r\n\x1a\n':
|
||||
# self.image_file is a PNG file
|
||||
image_file.seek(16)
|
||||
img_size = (struct.unpack("!I", image_file.read(4))[0], struct.unpack("!I", image_file.read(4))[0])
|
||||
|
||||
if img_size > self.max_img_size:
|
||||
# The image exceeds the maximum image size, so resize it
|
||||
image = _resize_image(self.image_file, img_size)
|
||||
else:
|
||||
image = os.path.abspath(self.image_file)
|
||||
elif PILImage:
|
||||
# Pillow is installed, meaning the image can be converted automatically
|
||||
img = PILImage.open(self.image_file, mode='r')
|
||||
|
||||
if img.size > self.max_img_size:
|
||||
image = _resize_image(img, img.size)
|
||||
else:
|
||||
image_data = io.BytesIO()
|
||||
img.save(image_data, format='PNG')
|
||||
img.close()
|
||||
image = image_data.getvalue()
|
||||
logger.info("Converted image %s to PNG format", self.image_file)
|
||||
else:
|
||||
raise ValueError(
|
||||
"The image %s needs to be converted to a PNG file, but PIL.Image is not available! Either install the "
|
||||
"Pillow package, or use a PNG image for you splash screen." % (self.image_file,)
|
||||
)
|
||||
|
||||
image_file.close()
|
||||
|
||||
SplashWriter(
|
||||
self.name,
|
||||
self.splash_requirements,
|
||||
os.path.basename(self.tcl_lib), # tcl86t.dll
|
||||
os.path.basename(self.tk_lib), # tk86t.dll
|
||||
tcltk_info.TCL_ROOTNAME,
|
||||
tcltk_info.TK_ROOTNAME,
|
||||
image,
|
||||
self.script,
|
||||
self._CENTER_MODES[self.center],
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _check_tcl_tk_compatibility(tcltk_info):
|
||||
tcl_version = tcltk_info.tcl_version # (major, minor) tuple
|
||||
tk_version = tcltk_info.tk_version
|
||||
|
||||
if is_darwin and tcltk_info.is_macos_system_framework:
|
||||
# Outdated Tcl/Tk 8.5 system framework is not supported.
|
||||
raise SystemExit(
|
||||
"ERROR: The splash screen feature does not support macOS system framework version of Tcl/Tk."
|
||||
)
|
||||
|
||||
# Test if tcl/tk version is supported
|
||||
if tcl_version < (8, 6) or tk_version < (8, 6):
|
||||
logger.warning(
|
||||
"The installed Tcl/Tk (%d.%d / %d.%d) version might not work with the splash screen feature of the "
|
||||
"bootloader, which was tested against Tcl/Tk 8.6", *tcl_version, *tk_version
|
||||
)
|
||||
|
||||
# This should be impossible, since tcl/tk is released together with the same version number, but just in case
|
||||
if tcl_version != tk_version:
|
||||
logger.warning(
|
||||
"The installed version of Tcl (%d.%d) and Tk (%d.%d) do not match. PyInstaller is tested against "
|
||||
"matching versions", *tcl_version, *tk_version
|
||||
)
|
||||
|
||||
# Ensure that Tcl is built with multi-threading support.
|
||||
if not tcltk_info.tcl_threaded:
|
||||
# This is a feature breaking problem, so exit.
|
||||
raise SystemExit(
|
||||
"ERROR: The installed Tcl version is not threaded. PyInstaller only supports the splash screen "
|
||||
"using threaded Tcl."
|
||||
)
|
||||
|
||||
# Ensure that Tcl and Tk shared libraries are available
|
||||
if tcltk_info.tcl_shared_library is None or tcltk_info.tk_shared_library is None:
|
||||
message = \
|
||||
"ERROR: Could not determine the path to Tcl and/or Tk shared library, " \
|
||||
"which are required for splash screen."
|
||||
if not tcltk_info.tkinter_extension_file:
|
||||
message += (
|
||||
" The _tkinter module appears to be a built-in, which likely means that python was built with "
|
||||
"statically-linked Tcl/Tk libraries and is incompatible with splash screen."
|
||||
)
|
||||
raise SystemExit(message)
|
||||
|
||||
def generate_script(self):
|
||||
"""
|
||||
Generate the script for the splash screen.
|
||||
|
||||
If minify_script is True, all unnecessary parts will be removed.
|
||||
"""
|
||||
d = {}
|
||||
if self.text_pos is not None:
|
||||
logger.debug("Add text support to splash screen")
|
||||
d.update({
|
||||
'pad_x': self.text_pos[0],
|
||||
'pad_y': self.text_pos[1],
|
||||
'color': self.text_color,
|
||||
'font': self.text_font,
|
||||
'font_size': self.text_size,
|
||||
'default_text': self.text_default,
|
||||
})
|
||||
script = splash_templates.build_script(text_options=d, always_on_top=self.always_on_top)
|
||||
|
||||
if self.minify_script:
|
||||
# Remove any documentation, empty lines and unnecessary spaces
|
||||
script = '\n'.join(
|
||||
line for line in map(lambda line: line.strip(), script.splitlines())
|
||||
if not line.startswith('#') # documentation
|
||||
and line # empty lines
|
||||
)
|
||||
# Remove unnecessary spaces
|
||||
script = re.sub(' +', ' ', script)
|
||||
|
||||
# Write script to disk, so that it is transparent to the user what script is executed.
|
||||
with open(self.script_name, "w", encoding="utf-8") as script_file:
|
||||
script_file.write(script)
|
||||
return script
|
||||
|
||||
@staticmethod
|
||||
def _uses_tkinter(tkinter_file, binaries):
|
||||
# Test for _tkinter extension instead of tkinter module, because user might use a different wrapping library for
|
||||
# Tk. Use `pathlib.PurePath` in comparisons to account for case normalization and separator normalization.
|
||||
tkinter_file = pathlib.PurePath(tkinter_file)
|
||||
for dest_name, src_name, typecode in binaries:
|
||||
if pathlib.PurePath(src_name) == tkinter_file:
|
||||
return True
|
||||
return False
|
||||
@@ -0,0 +1,243 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
# -----------------------------------------------------------------------------
|
||||
"""
|
||||
Templates for the splash screen tcl script.
|
||||
"""
|
||||
from PyInstaller.compat import is_cygwin, is_darwin, is_win
|
||||
|
||||
ipc_script = r"""
|
||||
proc _ipc_server {channel clientaddr clientport} {
|
||||
# This function is called if a new client connects to
|
||||
# the server. This creates a channel, which calls
|
||||
# _ipc_caller if data was send through the connection
|
||||
set client_name [format <%s:%d> $clientaddr $clientport]
|
||||
|
||||
chan configure $channel \
|
||||
-buffering none \
|
||||
-encoding utf-8 \
|
||||
-eofchar \x04 \
|
||||
-translation cr
|
||||
chan event $channel readable [list _ipc_caller $channel $client_name]
|
||||
}
|
||||
|
||||
proc _ipc_caller {channel client_name} {
|
||||
# This function is called if a command was sent through
|
||||
# the tcp connection. The current implementation supports
|
||||
# two commands: update_text and exit, although exit
|
||||
# is implemented to be called if the connection gets
|
||||
# closed (from python) or the character 0x04 was received
|
||||
chan gets $channel cmd
|
||||
|
||||
if {[chan eof $channel]} {
|
||||
# This is entered if either the connection was closed
|
||||
# or the char 0x04 was send
|
||||
chan close $channel
|
||||
exit
|
||||
|
||||
} elseif {![chan blocked $channel]} {
|
||||
# RPC methods
|
||||
|
||||
# update_text command
|
||||
if {[string match "update_text*" $cmd]} {
|
||||
global status_text
|
||||
set first [expr {[string first "(" $cmd] + 1}]
|
||||
set last [expr {[string last ")" $cmd] - 1}]
|
||||
|
||||
set status_text [string range $cmd $first $last]
|
||||
}
|
||||
# Implement other procedures here
|
||||
}
|
||||
}
|
||||
|
||||
# By setting the port to 0 the os will assign a free port
|
||||
set server_socket [socket -server _ipc_server -myaddr localhost 0]
|
||||
set server_port [fconfigure $server_socket -sockname]
|
||||
|
||||
# This environment variable is shared between the python and the tcl
|
||||
# interpreter and publishes the port the tcp server socket is available
|
||||
set env(_PYI_SPLASH_IPC) [lindex $server_port 2]
|
||||
"""
|
||||
|
||||
image_script = r"""
|
||||
# The variable $_image_data, which holds the data for the splash
|
||||
# image is created by the bootloader.
|
||||
image create photo splash_image
|
||||
splash_image put $_image_data
|
||||
# delete the variable, because the image now holds the data
|
||||
unset _image_data
|
||||
|
||||
proc canvas_text_update {canvas tag _var - -} {
|
||||
# This function is rigged to be called if the a variable
|
||||
# status_text gets changed. This updates the text on
|
||||
# the canvas
|
||||
upvar $_var var
|
||||
$canvas itemconfigure $tag -text $var
|
||||
}
|
||||
"""
|
||||
|
||||
splash_canvas_setup = r"""
|
||||
package require Tk
|
||||
|
||||
set image_width [image width splash_image]
|
||||
set image_height [image height splash_image]
|
||||
|
||||
# If bootloader set $_pyi_screen_geometry array (advanced splash-screen
|
||||
# centering modes that are available on some platforms), use values from it.
|
||||
# Otherwise, try to center splash screen using information obtained via
|
||||
# winfo screenwidth and screenheight command.
|
||||
if {[info exists _pyi_screen_geometry]} {
|
||||
set display_x $_pyi_screen_geometry(x)
|
||||
set display_y $_pyi_screen_geometry(y)
|
||||
set display_width $_pyi_screen_geometry(width)
|
||||
set display_height $_pyi_screen_geometry(height)
|
||||
} else {
|
||||
set display_x 0
|
||||
set display_y 0
|
||||
set display_width [winfo screenwidth .]
|
||||
set display_height [winfo screenheight .]
|
||||
}
|
||||
|
||||
set x_position [expr {int($display_x + 0.5*($display_width - $image_width))}]
|
||||
set y_position [expr {int($display_y + 0.5*($display_height - $image_height))}]
|
||||
|
||||
# Toplevel frame in which all widgets should be positioned
|
||||
frame .root
|
||||
|
||||
# Configure the canvas on which the splash
|
||||
# screen will be drawn
|
||||
canvas .root.canvas \
|
||||
-width $image_width \
|
||||
-height $image_height \
|
||||
-borderwidth 0 \
|
||||
-highlightthickness 0
|
||||
|
||||
# Draw the image into the canvas, filling it.
|
||||
.root.canvas create image \
|
||||
[expr {$image_width / 2}] \
|
||||
[expr {$image_height / 2}] \
|
||||
-image splash_image
|
||||
"""
|
||||
|
||||
splash_canvas_text = r"""
|
||||
# Create a text on the canvas, which tracks the local
|
||||
# variable status_text. status_text is changed via C to
|
||||
# update the progress on the splash screen.
|
||||
# We cannot use the default label, because it has a
|
||||
# default background, which cannot be turned transparent
|
||||
.root.canvas create text \
|
||||
%(pad_x)d \
|
||||
%(pad_y)d \
|
||||
-fill %(color)s \
|
||||
-justify center \
|
||||
-font myFont \
|
||||
-tag vartext \
|
||||
-anchor sw
|
||||
trace add variable status_text write \
|
||||
[list canvas_text_update .root.canvas vartext]
|
||||
set status_text "%(default_text)s"
|
||||
"""
|
||||
|
||||
splash_canvas_default_font = r"""
|
||||
font create myFont {*}[font actual TkDefaultFont]
|
||||
font configure myFont -size %(font_size)d
|
||||
"""
|
||||
|
||||
splash_canvas_custom_font = r"""
|
||||
font create myFont -family %(font)s -size %(font_size)d
|
||||
"""
|
||||
|
||||
if is_win or is_cygwin:
|
||||
transparent_setup = r"""
|
||||
# If the image is transparent, the background will be filled
|
||||
# with magenta. The magenta background is later replaced with transparency.
|
||||
# Here is the limitation of this implementation, that only
|
||||
# sharp transparent image corners are possible
|
||||
wm attributes . -transparentcolor magenta
|
||||
.root.canvas configure -background magenta
|
||||
"""
|
||||
|
||||
elif is_darwin:
|
||||
# This is untested, but should work following: https://stackoverflow.com/a/44296157/5869139
|
||||
transparent_setup = r"""
|
||||
wm attributes . -transparent 1
|
||||
. configure -background systemTransparent
|
||||
.root.canvas configure -background systemTransparent
|
||||
"""
|
||||
|
||||
else:
|
||||
# For Linux there is no common way to create a transparent window
|
||||
transparent_setup = r""
|
||||
|
||||
pack_widgets = r"""
|
||||
# Position all widgets in the window
|
||||
pack .root
|
||||
grid .root.canvas -column 0 -row 0 -columnspan 1 -rowspan 2
|
||||
"""
|
||||
|
||||
# Enable always-on-top behavior, by setting overrideredirect and the topmost attribute.
|
||||
position_window_on_top = r"""
|
||||
# Set position and mode of the window - always-on-top behavior
|
||||
wm overrideredirect . 1
|
||||
wm geometry . +${x_position}+${y_position}
|
||||
wm attributes . -topmost 1
|
||||
"""
|
||||
|
||||
# Disable always-on-top behavior
|
||||
if is_win or is_cygwin or is_darwin:
|
||||
# On Windows, we disable the always-on-top behavior while still setting overrideredirect
|
||||
# (to disable window decorations), but set topmost attribute to 0.
|
||||
position_window = r"""
|
||||
# Set position and mode of the window
|
||||
wm overrideredirect . 1
|
||||
wm geometry . +${x_position}+${y_position}
|
||||
wm attributes . -topmost 0
|
||||
"""
|
||||
else:
|
||||
# On Linux, we must not use overrideredirect; instead, we set X11-specific type attribute to splash,
|
||||
# which lets the window manager to properly handle the splash screen (without window decorations
|
||||
# but allowing other windows to be brought to front).
|
||||
position_window = r"""
|
||||
# Set position and mode of the window
|
||||
wm geometry . +${x_position}+${y_position}
|
||||
wm attributes . -type splash
|
||||
"""
|
||||
|
||||
raise_window = r"""
|
||||
raise .
|
||||
"""
|
||||
|
||||
|
||||
def build_script(text_options=None, always_on_top=False):
|
||||
"""
|
||||
This function builds the tcl script for the splash screen.
|
||||
"""
|
||||
# Order is important!
|
||||
script = [
|
||||
ipc_script,
|
||||
image_script,
|
||||
splash_canvas_setup,
|
||||
]
|
||||
|
||||
if text_options:
|
||||
# If the default font is used we need a different syntax
|
||||
if text_options['font'] == "TkDefaultFont":
|
||||
script.append(splash_canvas_default_font % text_options)
|
||||
else:
|
||||
script.append(splash_canvas_custom_font % text_options)
|
||||
script.append(splash_canvas_text % text_options)
|
||||
|
||||
script.append(transparent_setup)
|
||||
|
||||
script.append(pack_widgets)
|
||||
script.append(position_window_on_top if always_on_top else position_window)
|
||||
script.append(raise_window)
|
||||
|
||||
return '\n'.join(script)
|
||||
@@ -0,0 +1,126 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Templates to generate .spec files.
|
||||
"""
|
||||
|
||||
onefiletmplt = """# -*- mode: python ; coding: utf-8 -*-
|
||||
%(preamble)s
|
||||
|
||||
a = Analysis(
|
||||
%(scripts)s,
|
||||
pathex=%(pathex)s,
|
||||
binaries=%(binaries)s,
|
||||
datas=%(datas)s,
|
||||
hiddenimports=%(hiddenimports)s,
|
||||
hookspath=%(hookspath)r,
|
||||
hooksconfig={},
|
||||
runtime_hooks=%(runtime_hooks)r,
|
||||
excludes=%(excludes)s,
|
||||
noarchive=%(noarchive)s,
|
||||
optimize=%(optimize)r,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
%(splash_init)s
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,%(splash_target)s%(splash_binaries)s
|
||||
%(options)s,
|
||||
name='%(name)s',
|
||||
debug=%(debug_bootloader)s,
|
||||
bootloader_ignore_signals=%(bootloader_ignore_signals)s,
|
||||
strip=%(strip)s,
|
||||
upx=%(upx)s,
|
||||
upx_exclude=%(upx_exclude)s,
|
||||
runtime_tmpdir=%(runtime_tmpdir)r,
|
||||
console=%(console)s,
|
||||
disable_windowed_traceback=%(disable_windowed_traceback)s,
|
||||
argv_emulation=%(argv_emulation)r,
|
||||
target_arch=%(target_arch)r,
|
||||
codesign_identity=%(codesign_identity)r,
|
||||
entitlements_file=%(entitlements_file)r,%(exe_options)s
|
||||
)
|
||||
"""
|
||||
|
||||
onedirtmplt = """# -*- mode: python ; coding: utf-8 -*-
|
||||
%(preamble)s
|
||||
|
||||
a = Analysis(
|
||||
%(scripts)s,
|
||||
pathex=%(pathex)s,
|
||||
binaries=%(binaries)s,
|
||||
datas=%(datas)s,
|
||||
hiddenimports=%(hiddenimports)s,
|
||||
hookspath=%(hookspath)r,
|
||||
hooksconfig={},
|
||||
runtime_hooks=%(runtime_hooks)r,
|
||||
excludes=%(excludes)s,
|
||||
noarchive=%(noarchive)s,
|
||||
optimize=%(optimize)r,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
%(splash_init)s
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,%(splash_target)s
|
||||
%(options)s,
|
||||
exclude_binaries=True,
|
||||
name='%(name)s',
|
||||
debug=%(debug_bootloader)s,
|
||||
bootloader_ignore_signals=%(bootloader_ignore_signals)s,
|
||||
strip=%(strip)s,
|
||||
upx=%(upx)s,
|
||||
console=%(console)s,
|
||||
disable_windowed_traceback=%(disable_windowed_traceback)s,
|
||||
argv_emulation=%(argv_emulation)r,
|
||||
target_arch=%(target_arch)r,
|
||||
codesign_identity=%(codesign_identity)r,
|
||||
entitlements_file=%(entitlements_file)r,%(exe_options)s
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,%(splash_binaries)s
|
||||
strip=%(strip)s,
|
||||
upx=%(upx)s,
|
||||
upx_exclude=%(upx_exclude)s,
|
||||
name='%(name)s',
|
||||
)
|
||||
"""
|
||||
|
||||
bundleexetmplt = """app = BUNDLE(
|
||||
exe,
|
||||
name='%(name)s.app',
|
||||
icon=%(icon)s,
|
||||
bundle_identifier=%(bundle_identifier)s,
|
||||
)
|
||||
"""
|
||||
|
||||
bundletmplt = """app = BUNDLE(
|
||||
coll,
|
||||
name='%(name)s.app',
|
||||
icon=%(icon)s,
|
||||
bundle_identifier=%(bundle_identifier)s,
|
||||
)
|
||||
"""
|
||||
|
||||
splashtmpl = """splash = Splash(
|
||||
%(splash_image)r,
|
||||
binaries=a.binaries,
|
||||
datas=a.datas,
|
||||
text_pos=None,
|
||||
text_size=12,
|
||||
minify_script=True,
|
||||
always_on_top=True,%(splash_options)s
|
||||
)
|
||||
"""
|
||||
853
venv/lib/python3.12/site-packages/PyInstaller/building/utils.py
Normal file
853
venv/lib/python3.12/site-packages/PyInstaller/building/utils.py
Normal file
@@ -0,0 +1,853 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
import fnmatch
|
||||
import glob
|
||||
import hashlib
|
||||
import io
|
||||
import marshal
|
||||
import os
|
||||
import pathlib
|
||||
import platform
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import types
|
||||
import zipfile
|
||||
|
||||
from PyInstaller import compat
|
||||
from PyInstaller import log as logging
|
||||
from PyInstaller.compat import is_aix, is_darwin, is_win, is_linux, is_termux
|
||||
from PyInstaller.exceptions import InvalidSrcDestTupleError
|
||||
from PyInstaller.utils import misc
|
||||
|
||||
if is_win:
|
||||
from PyInstaller.utils.win32 import versioninfo
|
||||
|
||||
if is_darwin:
|
||||
import PyInstaller.utils.osx as osxutils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# -- Helpers for checking guts.
|
||||
#
|
||||
# NOTE: by _GUTS it is meant intermediate files and data structures that PyInstaller creates for bundling files and
|
||||
# creating final executable.
|
||||
|
||||
|
||||
def _check_guts_eq(attr_name, old_value, new_value, last_build):
|
||||
"""
|
||||
Rebuild is required if values differ.
|
||||
"""
|
||||
if old_value != new_value:
|
||||
logger.info("Building because %s changed", attr_name)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _check_guts_toc_mtime(attr_name, old_toc, new_toc, last_build):
|
||||
"""
|
||||
Rebuild is required if mtimes of files listed in old TOC are newer than last_build.
|
||||
|
||||
Use this for calculated/analysed values read from cache.
|
||||
"""
|
||||
for dest_name, src_name, typecode in old_toc:
|
||||
if misc.mtime(src_name) > last_build:
|
||||
logger.info("Building because %s changed", src_name)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _check_guts_toc(attr_name, old_toc, new_toc, last_build):
|
||||
"""
|
||||
Rebuild is required if either TOC content changed or mtimes of files listed in old TOC are newer than last_build.
|
||||
|
||||
Use this for input parameters.
|
||||
"""
|
||||
return _check_guts_eq(attr_name, old_toc, new_toc, last_build) or \
|
||||
_check_guts_toc_mtime(attr_name, old_toc, new_toc, last_build)
|
||||
|
||||
|
||||
def destination_name_for_extension(module_name, src_name, typecode):
|
||||
"""
|
||||
Take a TOC entry (dest_name, src_name, typecode) and determine the full destination name for the extension.
|
||||
"""
|
||||
|
||||
assert typecode == 'EXTENSION'
|
||||
|
||||
# The `module_name` should be the extension's importable module name, such as `psutil._psutil_linux` or
|
||||
# `numpy._core._multiarray_umath`. Reconstruct the directory structure from parent package name(s), if any.
|
||||
dest_elements = module_name.split('.')
|
||||
|
||||
# We have the base name of the extension file (the last element in the module name), but we do not know the
|
||||
# full extension suffix. We can take that from source name; for simplicity, replace the whole base name part.
|
||||
src_path = pathlib.Path(src_name)
|
||||
dest_elements[-1] = src_path.name
|
||||
|
||||
# Extensions that originate from python's python3.x/lib-dynload directory should be diverted into
|
||||
# python3.x/lib-dynload destination directory instead of being collected into top-level application directory.
|
||||
# See #5604 for original motivation (using just lib-dynload), and #9204 for extension (using python3.x/lib-dynload).
|
||||
if src_path.parent.name == 'lib-dynload':
|
||||
python_dir = f'python{sys.version_info.major}.{sys.version_info.minor}'
|
||||
if src_path.parent.parent.name == python_dir:
|
||||
dest_elements = [python_dir, 'lib-dynload', *dest_elements]
|
||||
|
||||
return os.path.join(*dest_elements)
|
||||
|
||||
|
||||
def process_collected_binary(
|
||||
src_name,
|
||||
dest_name,
|
||||
use_strip=False,
|
||||
use_upx=False,
|
||||
upx_exclude=None,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
strict_arch_validation=False
|
||||
):
|
||||
"""
|
||||
Process the collected binary using strip or UPX (or both), and apply any platform-specific processing. On macOS,
|
||||
this rewrites the library paths in the headers, and (re-)signs the binary. On-disk cache is used to avoid processing
|
||||
the same binary with same options over and over.
|
||||
|
||||
In addition to given arguments, this function also uses CONF['cachedir'] and CONF['upx_dir'].
|
||||
"""
|
||||
from PyInstaller.config import CONF
|
||||
|
||||
# We need to use cache in the following scenarios:
|
||||
# * extra binary processing due to use of `strip` or `upx`
|
||||
# * building on macOS, where we need to rewrite library paths in binaries' headers and (re-)sign the binaries.
|
||||
if not use_strip and not use_upx and not is_darwin:
|
||||
return src_name
|
||||
|
||||
# Match against provided UPX exclude patterns.
|
||||
upx_exclude = upx_exclude or []
|
||||
if use_upx:
|
||||
src_path = pathlib.PurePath(src_name)
|
||||
for upx_exclude_entry in upx_exclude:
|
||||
# pathlib.PurePath.match() matches from right to left, and supports * wildcard, but does not support the
|
||||
# "**" syntax for directory recursion. Case sensitivity follows the OS default.
|
||||
if src_path.match(upx_exclude_entry):
|
||||
logger.info("Disabling UPX for %s due to match in exclude pattern: %s", src_name, upx_exclude_entry)
|
||||
use_upx = False
|
||||
break
|
||||
|
||||
# Additional automatic disablement rules for UPX and strip.
|
||||
|
||||
# On Windows, avoid using UPX with binaries that have control flow guard (CFG) enabled.
|
||||
if use_upx and is_win and versioninfo.pefile_check_control_flow_guard(src_name):
|
||||
logger.info('Disabling UPX for %s due to CFG!', src_name)
|
||||
use_upx = False
|
||||
|
||||
# Avoid using UPX with Qt plugins, as it strips the data required by the Qt plugin loader.
|
||||
if use_upx and misc.is_file_qt_plugin(src_name):
|
||||
logger.info('Disabling UPX for %s due to it being a Qt plugin!', src_name)
|
||||
use_upx = False
|
||||
|
||||
# On linux, if a binary has an accompanying HMAC or CHK file, avoid modifying it in any way.
|
||||
if (use_upx or use_strip) and is_linux:
|
||||
src_path = pathlib.Path(src_name)
|
||||
hmac_path = src_path.with_name(f".{src_path.name}.hmac")
|
||||
chk_path = src_path.with_suffix(".chk")
|
||||
if hmac_path.is_file():
|
||||
logger.info('Disabling UPX and/or strip for %s due to accompanying .hmac file!', src_name)
|
||||
use_upx = use_strip = False
|
||||
elif chk_path.is_file():
|
||||
logger.info('Disabling UPX and/or strip for %s due to accompanying .chk file!', src_name)
|
||||
use_upx = use_strip = False
|
||||
del src_path, hmac_path, chk_path
|
||||
|
||||
# Exit early if no processing is required after above rules are applied.
|
||||
if not use_strip and not use_upx and not is_darwin:
|
||||
return src_name
|
||||
|
||||
# Prepare cache directory path. Cache is tied to python major/minor version, but also to various processing options.
|
||||
pyver = f'py{sys.version_info[0]}{sys.version_info[1]}'
|
||||
arch = platform.architecture()[0]
|
||||
cache_dir = os.path.join(
|
||||
CONF['cachedir'],
|
||||
f'bincache{use_strip:d}{use_upx:d}{pyver}{arch}',
|
||||
)
|
||||
if target_arch:
|
||||
cache_dir = os.path.join(cache_dir, target_arch)
|
||||
if is_darwin:
|
||||
# Separate by codesign identity
|
||||
if codesign_identity:
|
||||
# Compute hex digest of codesign identity string to prevent issues with invalid characters.
|
||||
csi_hash = hashlib.sha256(codesign_identity.encode('utf-8'))
|
||||
cache_dir = os.path.join(cache_dir, csi_hash.hexdigest())
|
||||
else:
|
||||
cache_dir = os.path.join(cache_dir, 'adhoc') # ad-hoc signing
|
||||
# Separate by entitlements
|
||||
if entitlements_file:
|
||||
# Compute hex digest of entitlements file contents
|
||||
with open(entitlements_file, 'rb') as fp:
|
||||
ef_hash = hashlib.sha256(fp.read())
|
||||
cache_dir = os.path.join(cache_dir, ef_hash.hexdigest())
|
||||
else:
|
||||
cache_dir = os.path.join(cache_dir, 'no-entitlements')
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
|
||||
# Load cache index, if available
|
||||
cache_index_file = os.path.join(cache_dir, "index.dat")
|
||||
try:
|
||||
cache_index = misc.load_py_data_struct(cache_index_file)
|
||||
except FileNotFoundError:
|
||||
cache_index = {}
|
||||
except Exception:
|
||||
# Tell the user they may want to fix their cache... However, do not delete it for them; if it keeps getting
|
||||
# corrupted, we will never find out.
|
||||
logger.warning("PyInstaller bincache may be corrupted; use pyinstaller --clean to fix it.")
|
||||
raise
|
||||
|
||||
# Look up the file in cache; use case-normalized destination name as identifier.
|
||||
cached_id = os.path.normcase(dest_name)
|
||||
cached_name = os.path.join(cache_dir, dest_name)
|
||||
src_digest = _compute_file_digest(src_name)
|
||||
|
||||
if cached_id in cache_index:
|
||||
# If digest matches to the cached digest, return the cached file...
|
||||
if src_digest == cache_index[cached_id]:
|
||||
return cached_name
|
||||
|
||||
# ... otherwise remove it.
|
||||
os.remove(cached_name)
|
||||
|
||||
# Ensure parent path exists
|
||||
os.makedirs(os.path.dirname(cached_name), exist_ok=True)
|
||||
|
||||
# Use `shutil.copyfile` to copy the file with default permissions bits, then manually set executable
|
||||
# bits. This way, we avoid copying permission bits and metadata from the original file, which might be too
|
||||
# restrictive for further processing (read-only permissions, immutable flag on FreeBSD, and so on).
|
||||
shutil.copyfile(src_name, cached_name)
|
||||
os.chmod(cached_name, 0o755)
|
||||
|
||||
# Apply strip
|
||||
if use_strip:
|
||||
strip_options = []
|
||||
if is_darwin:
|
||||
# The default strip behavior breaks some shared libraries under macOS.
|
||||
strip_options = ["-S"] # -S = strip only debug symbols.
|
||||
elif is_aix:
|
||||
# Set -X32_64 flag to have strip transparently process both 32-bit and 64-bit binaries, without user having
|
||||
# to set OBJECT_MODE environment variable prior to the build. Also accommodates potential mixed-case
|
||||
# scenario, for example a 32-bit utility program being collected into a 64-bit application bundle.
|
||||
strip_options = ["-X32_64"]
|
||||
|
||||
cmd = ["strip", *strip_options, cached_name]
|
||||
logger.info("Executing: %s", " ".join(cmd))
|
||||
try:
|
||||
p = subprocess.run(
|
||||
cmd,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
check=True,
|
||||
errors='ignore',
|
||||
encoding='utf-8',
|
||||
)
|
||||
logger.debug("Output from strip command:\n%s", p.stdout)
|
||||
except subprocess.CalledProcessError as e:
|
||||
show_warning = True
|
||||
|
||||
# On AIX, strip utility raises an error when ran against already-stripped binary. Catch the corresponding
|
||||
# message (`0654-419 The specified archive file was already stripped.`) and suppress the warning.
|
||||
if is_aix and "0654-419" in e.stdout:
|
||||
show_warning = False
|
||||
|
||||
if show_warning:
|
||||
logger.warning("Failed to run strip on %r!", cached_name, exc_info=True)
|
||||
logger.warning("Output from strip command:\n%s", e.stdout)
|
||||
except Exception:
|
||||
logger.warning("Failed to run strip on %r!", cached_name, exc_info=True)
|
||||
|
||||
# Apply UPX
|
||||
if use_upx:
|
||||
upx_exe = 'upx'
|
||||
upx_dir = CONF['upx_dir']
|
||||
if upx_dir:
|
||||
upx_exe = os.path.join(upx_dir, upx_exe)
|
||||
|
||||
upx_options = [
|
||||
# Do not compress icons, so that they can still be accessed externally.
|
||||
'--compress-icons=0',
|
||||
# Use LZMA compression.
|
||||
'--lzma',
|
||||
# Quiet mode.
|
||||
'-q',
|
||||
]
|
||||
if is_win:
|
||||
# Binaries built with Visual Studio 7.1 require --strip-loadconf or they will not compress.
|
||||
upx_options.append('--strip-loadconf')
|
||||
|
||||
cmd = [upx_exe, *upx_options, cached_name]
|
||||
logger.info("Executing: %s", " ".join(cmd))
|
||||
try:
|
||||
p = subprocess.run(
|
||||
cmd,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
check=True,
|
||||
errors='ignore',
|
||||
encoding='utf-8',
|
||||
)
|
||||
logger.debug("Output from upx command:\n%s", p.stdout)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.warning("Failed to upx strip on %r!", cached_name, exc_info=True)
|
||||
logger.warning("Output from upx command:\n%s", e.stdout)
|
||||
except Exception:
|
||||
logger.warning("Failed to run upx on %r!", cached_name, exc_info=True)
|
||||
|
||||
# On macOS, we need to modify the given binary's paths to the dependent libraries, in order to ensure they are
|
||||
# relocatable and always refer to location within the frozen application. Specifically, we make all dependent
|
||||
# library paths relative to @rpath, and set @rpath to point to the top-level application directory, relative to
|
||||
# the binary's location (i.e., @loader_path).
|
||||
#
|
||||
# While modifying the headers invalidates existing signatures, we avoid removing them in order to speed things up
|
||||
# (and to avoid potential bugs in the codesign utility, like the one reported on macOS 10.13 in #6167).
|
||||
# The forced re-signing at the end should take care of the invalidated signatures.
|
||||
if is_darwin:
|
||||
try:
|
||||
osxutils.binary_to_target_arch(cached_name, target_arch, display_name=src_name)
|
||||
#osxutils.remove_signature_from_binary(cached_name) # Disabled as per comment above.
|
||||
target_rpath = str(
|
||||
pathlib.PurePath('@loader_path', *['..' for level in pathlib.PurePath(dest_name).parent.parts])
|
||||
)
|
||||
osxutils.set_dylib_dependency_paths(cached_name, target_rpath)
|
||||
osxutils.sign_binary(cached_name, codesign_identity, entitlements_file)
|
||||
except osxutils.InvalidBinaryError:
|
||||
# Raised by osxutils.binary_to_target_arch when the given file is not a valid macOS binary (for example,
|
||||
# a linux .so file; see issue #6327). The error prevents any further processing, so just ignore it.
|
||||
pass
|
||||
except osxutils.IncompatibleBinaryArchError:
|
||||
# Raised by osxutils.binary_to_target_arch when the given file does not contain (all) required arch slices.
|
||||
# Depending on the strict validation mode, re-raise or swallow the error.
|
||||
#
|
||||
# Strict validation should be enabled only for binaries where the architecture *must* match the target one,
|
||||
# i.e., the extension modules. Everything else is pretty much a gray area, for example:
|
||||
# * a universal2 extension may have its x86_64 and arm64 slices linked against distinct single-arch/thin
|
||||
# shared libraries
|
||||
# * a collected executable that is launched by python code via a subprocess can be x86_64-only, even though
|
||||
# the actual python code is running on M1 in native arm64 mode.
|
||||
if strict_arch_validation:
|
||||
raise
|
||||
logger.debug("File %s failed optional architecture validation - collecting as-is!", src_name)
|
||||
except Exception as e:
|
||||
raise SystemError(f"Failed to process binary {cached_name!r}!") from e
|
||||
|
||||
# Update cache index
|
||||
cache_index[cached_id] = src_digest
|
||||
misc.save_py_data_struct(cache_index_file, cache_index)
|
||||
|
||||
return cached_name
|
||||
|
||||
|
||||
def _compute_file_digest(filename):
|
||||
hasher = hashlib.sha1()
|
||||
with open(filename, "rb") as fp:
|
||||
for chunk in iter(lambda: fp.read(16 * 1024), b""):
|
||||
hasher.update(chunk)
|
||||
return bytearray(hasher.digest())
|
||||
|
||||
|
||||
def _check_path_overlap(path):
|
||||
"""
|
||||
Check that path does not overlap with WORKPATH or SPECPATH (i.e., WORKPATH and SPECPATH may not start with path,
|
||||
which could be caused by a faulty hand-edited specfile).
|
||||
|
||||
Raise SystemExit if there is overlap, return True otherwise
|
||||
"""
|
||||
from PyInstaller.config import CONF
|
||||
specerr = 0
|
||||
if CONF['workpath'].startswith(path):
|
||||
logger.error('Specfile error: The output path "%s" contains WORKPATH (%s)', path, CONF['workpath'])
|
||||
specerr += 1
|
||||
if CONF['specpath'].startswith(path):
|
||||
logger.error('Specfile error: The output path "%s" contains SPECPATH (%s)', path, CONF['specpath'])
|
||||
specerr += 1
|
||||
if specerr:
|
||||
raise SystemExit(
|
||||
'ERROR: Please edit/recreate the specfile (%s) and set a different output name (e.g. "dist").' %
|
||||
CONF['spec']
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def _make_clean_directory(path):
|
||||
"""
|
||||
Create a clean directory from the given directory name.
|
||||
"""
|
||||
if _check_path_overlap(path):
|
||||
if os.path.isdir(path) or os.path.isfile(path):
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError:
|
||||
_rmtree(path)
|
||||
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
|
||||
def _rmtree(path):
|
||||
"""
|
||||
Remove directory and all its contents, but only after user confirmation, or if the -y option is set.
|
||||
"""
|
||||
from PyInstaller.config import CONF
|
||||
if CONF['noconfirm']:
|
||||
choice = 'y'
|
||||
elif sys.stdout.isatty():
|
||||
choice = input(
|
||||
'WARNING: The output directory "%s" and ALL ITS CONTENTS will be REMOVED! Continue? (y/N)' % path
|
||||
)
|
||||
else:
|
||||
raise SystemExit(
|
||||
'ERROR: The output directory "%s" is not empty. Please remove all its contents or use the -y option (remove'
|
||||
' output directory without confirmation).' % path
|
||||
)
|
||||
if choice.strip().lower() == 'y':
|
||||
if not CONF['noconfirm']:
|
||||
print("On your own risk, you can use the option `--noconfirm` to get rid of this question.")
|
||||
logger.info('Removing dir %s', path)
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
raise SystemExit('User aborted')
|
||||
|
||||
|
||||
# TODO Refactor to prohibit empty target directories. As the docstring below documents, this function currently permits
|
||||
# the second item of each 2-tuple in "hook.datas" to be the empty string, in which case the target directory defaults to
|
||||
# the source directory's basename. However, this functionality is very fragile and hence bad. Instead:
|
||||
#
|
||||
# * An exception should be raised if such item is empty.
|
||||
# * All hooks currently passing the empty string for such item (e.g.,
|
||||
# "hooks/hook-babel.py", "hooks/hook-matplotlib.py") should be refactored
|
||||
# to instead pass such basename.
|
||||
def format_binaries_and_datas(binaries_or_datas, workingdir=None):
|
||||
"""
|
||||
Convert the passed list of hook-style 2-tuples into a returned set of `TOC`-style 2-tuples.
|
||||
|
||||
Elements of the passed list are 2-tuples `(source_dir_or_glob, target_dir)`.
|
||||
Elements of the returned set are 2-tuples `(target_file, source_file)`.
|
||||
For backwards compatibility, the order of elements in the former tuples are the reverse of the order of elements in
|
||||
the latter tuples!
|
||||
|
||||
Parameters
|
||||
----------
|
||||
binaries_or_datas : list
|
||||
List of hook-style 2-tuples (e.g., the top-level `binaries` and `datas` attributes defined by hooks) whose:
|
||||
* The first element is either:
|
||||
* A glob matching only the absolute or relative paths of source non-Python data files.
|
||||
* The absolute or relative path of a source directory containing only source non-Python data files.
|
||||
* The second element is the relative path of the target directory into which these source files will be
|
||||
recursively copied.
|
||||
|
||||
If the optional `workingdir` parameter is passed, source paths may be either absolute or relative; else, source
|
||||
paths _must_ be absolute.
|
||||
workingdir : str
|
||||
Optional absolute path of the directory to which all relative source paths in the `binaries_or_datas`
|
||||
parameter will be prepended by (and hence converted into absolute paths) _or_ `None` if these paths are to be
|
||||
preserved as relative. Defaults to `None`.
|
||||
|
||||
Returns
|
||||
----------
|
||||
set
|
||||
Set of `TOC`-style 2-tuples whose:
|
||||
* First element is the absolute or relative path of a target file.
|
||||
* Second element is the absolute or relative path of the corresponding source file to be copied to this target
|
||||
file.
|
||||
"""
|
||||
toc_datas = set()
|
||||
|
||||
for src_root_path_or_glob, trg_root_dir in binaries_or_datas:
|
||||
# Disallow empty source path. Those are typically result of errors, and result in implicit collection of the
|
||||
# whole current working directory, which is never a good idea.
|
||||
if not src_root_path_or_glob:
|
||||
raise InvalidSrcDestTupleError(
|
||||
(src_root_path_or_glob, trg_root_dir),
|
||||
"Empty SRC is not allowed when adding binary and data files, as it would result in collection of the "
|
||||
"whole current working directory."
|
||||
)
|
||||
if not trg_root_dir:
|
||||
raise InvalidSrcDestTupleError(
|
||||
(src_root_path_or_glob, trg_root_dir),
|
||||
"Empty DEST_DIR is not allowed - to collect files into application's top-level directory, use "
|
||||
f"{os.curdir!r}."
|
||||
)
|
||||
# Disallow absolute target paths, as well as target paths that would end up pointing outside of the
|
||||
# application's top-level directory.
|
||||
if os.path.isabs(trg_root_dir):
|
||||
raise InvalidSrcDestTupleError((src_root_path_or_glob, trg_root_dir), "DEST_DIR must be a relative path!")
|
||||
if os.path.normpath(trg_root_dir).startswith('..'):
|
||||
raise InvalidSrcDestTupleError(
|
||||
(src_root_path_or_glob, trg_root_dir),
|
||||
"DEST_DIR must not point outside of application's top-level directory!",
|
||||
)
|
||||
|
||||
# Convert relative to absolute paths if required.
|
||||
if workingdir and not os.path.isabs(src_root_path_or_glob):
|
||||
src_root_path_or_glob = os.path.join(workingdir, src_root_path_or_glob)
|
||||
|
||||
# Normalize paths.
|
||||
src_root_path_or_glob = os.path.normpath(src_root_path_or_glob)
|
||||
|
||||
# If given source path is a file or directory path, pass it on.
|
||||
# If not, treat it as a glob and pass on all matching paths. However, we need to preserve the directories
|
||||
# captured by the glob - as opposed to collecting their contents into top-level target directory. Therefore,
|
||||
# we set a flag which is used in subsequent processing to distinguish between original directory paths and
|
||||
# directory paths that were captured by the glob.
|
||||
if os.path.isfile(src_root_path_or_glob) or os.path.isdir(src_root_path_or_glob):
|
||||
src_root_paths = [src_root_path_or_glob]
|
||||
was_glob = False
|
||||
else:
|
||||
src_root_paths = glob.glob(src_root_path_or_glob)
|
||||
was_glob = True
|
||||
|
||||
if not src_root_paths:
|
||||
raise SystemExit(f'ERROR: Unable to find {src_root_path_or_glob!r} when adding binary and data files.')
|
||||
|
||||
for src_root_path in src_root_paths:
|
||||
if os.path.isfile(src_root_path):
|
||||
# Normalizing the result to remove redundant relative paths (e.g., removing "./" from "trg/./file").
|
||||
toc_datas.add((
|
||||
os.path.normpath(os.path.join(trg_root_dir, os.path.basename(src_root_path))),
|
||||
os.path.normpath(src_root_path),
|
||||
))
|
||||
elif os.path.isdir(src_root_path):
|
||||
for src_dir, src_subdir_basenames, src_file_basenames in os.walk(src_root_path):
|
||||
# Ensure the current source directory is a subdirectory of the passed top-level source directory.
|
||||
# Since os.walk() does *NOT* follow symlinks by default, this should be the case. (But let's make
|
||||
# sure.)
|
||||
assert src_dir.startswith(src_root_path)
|
||||
|
||||
# Relative path of the current target directory, obtained by:
|
||||
#
|
||||
# * Stripping the top-level source directory from the current source directory (e.g., removing
|
||||
# "/top" from "/top/dir").
|
||||
# * Normalizing the result to remove redundant relative paths (e.g., removing "./" from
|
||||
# "trg/./file").
|
||||
if was_glob:
|
||||
# Preserve directories captured by glob.
|
||||
rel_dir = os.path.relpath(src_dir, os.path.dirname(src_root_path))
|
||||
else:
|
||||
rel_dir = os.path.relpath(src_dir, src_root_path)
|
||||
trg_dir = os.path.normpath(os.path.join(trg_root_dir, rel_dir))
|
||||
|
||||
for src_file_basename in src_file_basenames:
|
||||
src_file = os.path.join(src_dir, src_file_basename)
|
||||
if os.path.isfile(src_file):
|
||||
# Normalize the result to remove redundant relative paths (e.g., removing "./" from
|
||||
# "trg/./file").
|
||||
toc_datas.add((
|
||||
os.path.normpath(os.path.join(trg_dir, src_file_basename)), os.path.normpath(src_file)
|
||||
))
|
||||
|
||||
return toc_datas
|
||||
|
||||
|
||||
def get_code_object(modname, filename, optimize):
|
||||
"""
|
||||
Get the code-object for a module.
|
||||
|
||||
This is a simplifed non-performant version which circumvents __pycache__.
|
||||
"""
|
||||
|
||||
# Once upon a time, we compiled dummy code objects for PEP-420 namespace packages. We do not do that anymore.
|
||||
assert filename not in {'-', None}, "Called with PEP-420 namespace package!"
|
||||
|
||||
_, ext = os.path.splitext(filename)
|
||||
ext = ext.lower()
|
||||
|
||||
if ext == '.pyc':
|
||||
# The module is available in binary-only form. Read the contents of .pyc file using helper function, which
|
||||
# supports reading from either stand-alone or archive-embedded .pyc files.
|
||||
logger.debug('Reading code object from .pyc file %s', filename)
|
||||
pyc_data = _read_pyc_data(filename)
|
||||
code_object = marshal.loads(pyc_data[16:])
|
||||
else:
|
||||
# Assume this is a source .py file, but allow an arbitrary extension (other than .pyc, which is taken in
|
||||
# the above branch). This allows entry-point scripts to have an arbitrary (or no) extension, as tested by
|
||||
# the `test_arbitrary_ext` in `test_basic.py`.
|
||||
logger.debug('Compiling python script/module file %s', filename)
|
||||
|
||||
with open(filename, 'rb') as f:
|
||||
source = f.read()
|
||||
|
||||
# If entry-point script has no suffix, append .py when compiling the source. In POSIX builds, the executable
|
||||
# has no suffix either; this causes issues with `traceback` module, as it tries to read the executable file
|
||||
# when trying to look up the code for the entry-point script (when current working directory contains the
|
||||
# executable).
|
||||
_, ext = os.path.splitext(filename)
|
||||
if not ext:
|
||||
logger.debug("Appending .py to compiled entry-point name...")
|
||||
filename += '.py'
|
||||
|
||||
try:
|
||||
code_object = compile(source, filename, 'exec', optimize=optimize)
|
||||
except SyntaxError:
|
||||
logger.warning("Sytnax error while compiling %s", filename)
|
||||
raise
|
||||
|
||||
return code_object
|
||||
|
||||
|
||||
def replace_filename_in_code_object(code_object, filename):
|
||||
"""
|
||||
Recursively replace the `co_filename` in the given code object and code objects stored in its `co_consts` entries.
|
||||
Primarily used to anonymize collected code objects, i.e., by removing the build environment's paths from them.
|
||||
"""
|
||||
|
||||
consts = tuple(
|
||||
replace_filename_in_code_object(const_co, filename) if isinstance(const_co, types.CodeType) else const_co
|
||||
for const_co in code_object.co_consts
|
||||
)
|
||||
|
||||
return code_object.replace(co_consts=consts, co_filename=filename)
|
||||
|
||||
|
||||
def _should_include_system_binary(binary_tuple, exceptions):
|
||||
"""
|
||||
Return True if the given binary_tuple describes a system binary that should be included.
|
||||
|
||||
Exclude all system library binaries other than those with "lib-dynload" in the destination or "python" in the
|
||||
source, except for those matching the patterns in the exceptions list. Intended to be used from the Analysis
|
||||
exclude_system_libraries method.
|
||||
"""
|
||||
dest = binary_tuple[0]
|
||||
if dest.startswith(f'python{sys.version_info.major}.{sys.version_info.minor}/lib-dynload'):
|
||||
return True
|
||||
src = binary_tuple[1]
|
||||
if fnmatch.fnmatch(src, '*python*'):
|
||||
return True
|
||||
if is_termux:
|
||||
# Termux linux; system libraries are in /system/lib{,64} and /data/data/com.termux/files/usr/lib; in addition
|
||||
# /usr is symbolic link pointing at /data/data/com.termux/files/usr
|
||||
SYS_PREFIX = ('/system/lib', '/data/data/com.termux/files/usr/lib', '/usr/lib')
|
||||
else:
|
||||
# Standard linux distributions; system libraries are in /lib{,64} and /usr/lib{,64}
|
||||
SYS_PREFIX = ('/lib', '/usr/lib')
|
||||
if not src.startswith(SYS_PREFIX):
|
||||
return True
|
||||
for exception in exceptions:
|
||||
if fnmatch.fnmatch(dest, exception):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def compile_pymodule(name, src_path, workpath, optimize, code_cache=None):
|
||||
"""
|
||||
Given the name and source file for a pure-python module, compile the module in the specified working directory,
|
||||
and return the name of resulting .pyc file. The paths in the resulting .pyc module are anonymized by having their
|
||||
absolute prefix removed.
|
||||
|
||||
If a .pyc file with matching name already exists in the target working directory, it is re-used (provided it has
|
||||
compatible bytecode magic in the header, and that its modification time is newer than that of the source file).
|
||||
|
||||
If the specified module is available in binary-only form, the input .pyc file is copied to the target working
|
||||
directory and post-processed. If the specified module is available in source form, it is compiled only if
|
||||
corresponding code object is not available in the optional code-object cache; otherwise, it is copied from cache
|
||||
and post-processed. When compiling the module, the specified byte-code optimization level is used.
|
||||
|
||||
It is up to caller to ensure that the optional code-object cache contains only code-objects of target optimization
|
||||
level, and that if the specified working directory already contains .pyc files, that they were created with target
|
||||
optimization level.
|
||||
"""
|
||||
|
||||
# Construct the target .pyc filename in the workpath
|
||||
split_name = name.split(".")
|
||||
if "__init__" in src_path:
|
||||
# __init__ module; use "__init__" as module name, and construct parent path using all components of the
|
||||
# fully-qualified name
|
||||
parent_dirs = split_name
|
||||
mod_basename = "__init__"
|
||||
else:
|
||||
# Regular module; use last component of the fully-qualified name as module name, and the rest as the parent
|
||||
# path.
|
||||
parent_dirs = split_name[:-1]
|
||||
mod_basename = split_name[-1]
|
||||
pyc_path = os.path.join(workpath, *parent_dirs, mod_basename + '.pyc')
|
||||
|
||||
# Check if optional cache contains module entry
|
||||
code_object = code_cache.get(name, None) if code_cache else None
|
||||
|
||||
if code_object is None:
|
||||
_, ext = os.path.splitext(src_path)
|
||||
ext = ext.lower()
|
||||
|
||||
if ext == '.py':
|
||||
# Source py file; read source and compile it.
|
||||
with open(src_path, 'rb') as f:
|
||||
src_data = f.read()
|
||||
code_object = compile(src_data, src_path, 'exec', optimize=optimize)
|
||||
elif ext == '.pyc':
|
||||
# The module is available in binary-only form. Read the contents of .pyc file using helper function, which
|
||||
# supports reading from either stand-alone or archive-embedded .pyc files.
|
||||
pyc_data = _read_pyc_data(src_path)
|
||||
# Unmarshal code object; this is necessary if we want to strip paths from it
|
||||
code_object = marshal.loads(pyc_data[16:])
|
||||
else:
|
||||
raise ValueError(f"Invalid python module file {src_path}; unhandled extension {ext}!")
|
||||
|
||||
# Replace co_filename in code object with anonymized filename that does not contain full path. Construct the
|
||||
# relative filename from module name, similar how we earlier constructed the `pyc_path`.
|
||||
co_filename = os.path.join(*parent_dirs, mod_basename + '.py')
|
||||
code_object = replace_filename_in_code_object(code_object, co_filename)
|
||||
|
||||
# Write complete .pyc module to in-memory stream. Then, check if .pyc file already exists, compare contents, and
|
||||
# (re)write it only if different. This avoids unnecessary (re)writing of the file, and in turn also avoids
|
||||
# unnecessary cache invalidation for targets that make use of the .pyc file (e.g., PKG, COLLECT).
|
||||
with io.BytesIO() as pyc_stream:
|
||||
pyc_stream.write(compat.BYTECODE_MAGIC)
|
||||
pyc_stream.write(struct.pack('<I', 0b01)) # PEP-552: hash-based pyc, check_source=False
|
||||
pyc_stream.write(b'\00' * 8) # Zero the source hash
|
||||
marshal.dump(code_object, pyc_stream)
|
||||
pyc_data = pyc_stream.getvalue()
|
||||
|
||||
if os.path.isfile(pyc_path):
|
||||
with open(pyc_path, 'rb') as fh:
|
||||
existing_pyc_data = fh.read()
|
||||
if pyc_data == existing_pyc_data:
|
||||
return pyc_path # Return path to (existing) file.
|
||||
|
||||
# Ensure the existence of parent directories for the target pyc path
|
||||
os.makedirs(os.path.dirname(pyc_path), exist_ok=True)
|
||||
|
||||
# Write
|
||||
with open(pyc_path, 'wb') as fh:
|
||||
fh.write(pyc_data)
|
||||
|
||||
# Return output path
|
||||
return pyc_path
|
||||
|
||||
|
||||
def _read_pyc_data(filename):
|
||||
"""
|
||||
Helper for reading data from .pyc files. Supports both stand-alone and archive-embedded .pyc files. Used by
|
||||
`compile_pymodule` and `get_code_object` helper functions.
|
||||
"""
|
||||
src_file = pathlib.Path(filename)
|
||||
|
||||
if src_file.is_file():
|
||||
# Stand-alone .pyc file.
|
||||
pyc_data = src_file.read_bytes()
|
||||
else:
|
||||
# Check if .pyc file is stored in a .zip archive, as is the case for stdlib modules in embeddable
|
||||
# python on Windows.
|
||||
parent_zip_file = misc.path_to_parent_archive(src_file)
|
||||
if parent_zip_file is not None and zipfile.is_zipfile(parent_zip_file):
|
||||
with zipfile.ZipFile(parent_zip_file, 'r') as zip_archive:
|
||||
# NOTE: zip entry names must be in POSIX format, even on Windows!
|
||||
zip_entry_name = str(src_file.relative_to(parent_zip_file).as_posix())
|
||||
pyc_data = zip_archive.read(zip_entry_name)
|
||||
else:
|
||||
raise FileNotFoundError(f"Cannot find .pyc file {filename!r}!")
|
||||
|
||||
# Verify the python version
|
||||
if pyc_data[:4] != compat.BYTECODE_MAGIC:
|
||||
raise ValueError(f"The .pyc module {filename} was compiled for incompatible version of python!")
|
||||
|
||||
return pyc_data
|
||||
|
||||
|
||||
def postprocess_binaries_toc_pywin32(binaries):
|
||||
"""
|
||||
Process the given `binaries` TOC list to apply work around for `pywin32` package, fixing the target directory
|
||||
for collected extensions.
|
||||
"""
|
||||
# Ensure that all files collected from `win32` or `pythonwin` into top-level directory are put back into
|
||||
# their corresponding directories. They end up in top-level directory because `pywin32.pth` adds both
|
||||
# directories to the `sys.path`, so they end up visible as top-level directories. But these extensions
|
||||
# might in fact be linked against each other, so we should preserve the directory layout for consistency
|
||||
# between modulegraph-discovered extensions and linked binaries discovered by link-time dependency analysis.
|
||||
# Within the same framework, also consider `pywin32_system32`, just in case.
|
||||
PYWIN32_SUBDIRS = {'win32', 'pythonwin', 'pywin32_system32'}
|
||||
|
||||
processed_binaries = []
|
||||
for dest_name, src_name, typecode in binaries:
|
||||
dest_path = pathlib.PurePath(dest_name)
|
||||
src_path = pathlib.PurePath(src_name)
|
||||
|
||||
if dest_path.parent == pathlib.PurePath('.') and src_path.parent.name.lower() in PYWIN32_SUBDIRS:
|
||||
dest_path = pathlib.PurePath(src_path.parent.name) / dest_path
|
||||
dest_name = str(dest_path)
|
||||
|
||||
processed_binaries.append((dest_name, src_name, typecode))
|
||||
|
||||
return processed_binaries
|
||||
|
||||
|
||||
def postprocess_binaries_toc_pywin32_anaconda(binaries):
|
||||
"""
|
||||
Process the given `binaries` TOC list to apply work around for Anaconda `pywin32` package, fixing the location
|
||||
of collected `pywintypes3X.dll` and `pythoncom3X.dll`.
|
||||
"""
|
||||
# The Anaconda-provided `pywin32` package installs three copies of `pywintypes3X.dll` and `pythoncom3X.dll`,
|
||||
# located in the following directories (relative to the environment):
|
||||
# - Library/bin
|
||||
# - Lib/site-packages/pywin32_system32
|
||||
# - Lib/site-packages/win32
|
||||
#
|
||||
# This turns our dependency scanner and directory layout preservation mechanism into a lottery based on what
|
||||
# `pywin32` modules are imported and in what order. To keep things simple, we deal with this insanity by
|
||||
# post-processing the `binaries` list, modifying the destination of offending copies, and let the final TOC
|
||||
# list normalization deal with potential duplicates.
|
||||
DLL_CANDIDATES = {
|
||||
f"pywintypes{sys.version_info[0]}{sys.version_info[1]}.dll",
|
||||
f"pythoncom{sys.version_info[0]}{sys.version_info[1]}.dll",
|
||||
}
|
||||
|
||||
DUPLICATE_DIRS = {
|
||||
pathlib.PurePath('.'),
|
||||
pathlib.PurePath('win32'),
|
||||
}
|
||||
|
||||
processed_binaries = []
|
||||
for dest_name, src_name, typecode in binaries:
|
||||
# Check if we need to divert - based on the destination base name and destination parent directory.
|
||||
dest_path = pathlib.PurePath(dest_name)
|
||||
if dest_path.name.lower() in DLL_CANDIDATES and dest_path.parent in DUPLICATE_DIRS:
|
||||
dest_path = pathlib.PurePath("pywin32_system32") / dest_path.name
|
||||
dest_name = str(dest_path)
|
||||
|
||||
processed_binaries.append((dest_name, src_name, typecode))
|
||||
|
||||
return processed_binaries
|
||||
|
||||
|
||||
def create_base_library_zip(filename, modules_toc, code_cache=None):
|
||||
"""
|
||||
Create a zip archive with python modules that are needed during python interpreter initialization.
|
||||
"""
|
||||
with zipfile.ZipFile(filename, 'w') as zf:
|
||||
for name, src_path, typecode in modules_toc:
|
||||
# Obtain code object from cache, or compile it.
|
||||
code = None if code_cache is None else code_cache.get(name, None)
|
||||
if code is None:
|
||||
optim_level = {'PYMODULE': 0, 'PYMODULE-1': 1, 'PYMODULE-2': 2}[typecode]
|
||||
code = get_code_object(name, src_path, optimize=optim_level)
|
||||
# Determine destination name
|
||||
dest_name = name.replace('.', os.sep)
|
||||
# Special case: packages have an implied `__init__` filename that needs to be added.
|
||||
basename, ext = os.path.splitext(os.path.basename(src_path))
|
||||
if basename == '__init__':
|
||||
dest_name += os.sep + '__init__'
|
||||
dest_name += '.pyc' # Always .pyc, regardless of optimization level.
|
||||
# Replace full-path co_filename in code object with `dest_name` (and shorten suffix from .pyc to .py).
|
||||
code = replace_filename_in_code_object(code, dest_name[:-1])
|
||||
# Write the .pyc module
|
||||
with io.BytesIO() as fc:
|
||||
fc.write(compat.BYTECODE_MAGIC)
|
||||
fc.write(struct.pack('<I', 0b01)) # PEP-552: hash-based pyc, check_source=False
|
||||
fc.write(b'\00' * 8) # Match behavior of `building.utils.compile_pymodule`
|
||||
marshal.dump(code, fc)
|
||||
# Use a ZipInfo to set timestamp for deterministic build.
|
||||
info = zipfile.ZipInfo(dest_name)
|
||||
zf.writestr(info, fc.getvalue())
|
||||
733
venv/lib/python3.12/site-packages/PyInstaller/compat.py
Normal file
733
venv/lib/python3.12/site-packages/PyInstaller/compat.py
Normal file
@@ -0,0 +1,733 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
# ----------------------------------------------------------------------------
|
||||
"""
|
||||
Various classes and functions to provide some backwards-compatibility with previous versions of Python onward.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import errno
|
||||
|
||||
import importlib.machinery
|
||||
import importlib.util
|
||||
import os
|
||||
import platform
|
||||
import site
|
||||
import subprocess
|
||||
import sys
|
||||
import sysconfig
|
||||
import shutil
|
||||
import types
|
||||
|
||||
from PyInstaller._shared_with_waf import _pyi_machine
|
||||
from PyInstaller.exceptions import ExecCommandFailed
|
||||
|
||||
# hatch_build.py sets this environment variable to avoid errors due to unmet run-time dependencies. The
|
||||
# PyInstaller.compat module is imported by hatch_build.py to build wheels, and some dependencies that are otherwise
|
||||
# required at run-time (importlib-metadata on python < 3.10, pywin32-ctypes on Windows) might not be present while
|
||||
# building wheels, nor are they required during that phase.
|
||||
_setup_py_mode = os.environ.get('_PYINSTALLER_SETUP', '0') != '0'
|
||||
|
||||
# PyInstaller requires importlib.metadata from python >= 3.10 stdlib, or equivalent importlib-metadata >= 4.6.
|
||||
if _setup_py_mode:
|
||||
importlib_metadata = None
|
||||
else:
|
||||
if sys.version_info >= (3, 10):
|
||||
import importlib.metadata as importlib_metadata
|
||||
else:
|
||||
try:
|
||||
import importlib_metadata
|
||||
except ImportError as e:
|
||||
from PyInstaller.exceptions import ImportlibMetadataError
|
||||
raise ImportlibMetadataError() from e
|
||||
|
||||
import packaging.version # For importlib_metadata version check
|
||||
|
||||
# Validate the version
|
||||
if packaging.version.parse(importlib_metadata.version("importlib-metadata")) < packaging.version.parse("4.6"):
|
||||
from PyInstaller.exceptions import ImportlibMetadataError
|
||||
raise ImportlibMetadataError()
|
||||
|
||||
# Strict collect mode, which raises error when trying to collect duplicate files into PKG/CArchive or COLLECT.
|
||||
strict_collect_mode = os.environ.get("PYINSTALLER_STRICT_COLLECT_MODE", "0") != "0"
|
||||
|
||||
# Copied from https://docs.python.org/3/library/platform.html#cross-platform.
|
||||
is_64bits: bool = sys.maxsize > 2**32
|
||||
|
||||
# Distinguish specific code for various Python versions. Variables 'is_pyXY' mean that Python X.Y and up is supported.
|
||||
# Keep even unsupported versions here to keep 3rd-party hooks working.
|
||||
is_py35 = sys.version_info >= (3, 5)
|
||||
is_py36 = sys.version_info >= (3, 6)
|
||||
is_py37 = sys.version_info >= (3, 7)
|
||||
is_py38 = sys.version_info >= (3, 8)
|
||||
is_py39 = sys.version_info >= (3, 9)
|
||||
is_py310 = sys.version_info >= (3, 10)
|
||||
is_py311 = sys.version_info >= (3, 11)
|
||||
is_py312 = sys.version_info >= (3, 12)
|
||||
is_py313 = sys.version_info >= (3, 13)
|
||||
is_py314 = sys.version_info >= (3, 14)
|
||||
is_py315 = sys.version_info >= (3, 15)
|
||||
|
||||
is_win = sys.platform.startswith('win')
|
||||
is_win_10 = is_win and (platform.win32_ver()[0] == '10')
|
||||
is_win_11 = is_win and (platform.win32_ver()[0] == '11')
|
||||
is_win_wine = False # Running under Wine; determined later on.
|
||||
is_cygwin = sys.platform == 'cygwin'
|
||||
is_darwin = sys.platform == 'darwin' # macOS
|
||||
|
||||
# Unix platforms
|
||||
is_android = sys.platform.startswith('android')
|
||||
is_linux = sys.platform.startswith('linux') or is_android # For our intents and purposes, Android is also Linux.
|
||||
is_solar = sys.platform.startswith('sun') # Solaris
|
||||
is_aix = sys.platform.startswith('aix')
|
||||
is_freebsd = sys.platform.startswith('freebsd')
|
||||
is_openbsd = sys.platform.startswith('openbsd')
|
||||
is_hpux = sys.platform.startswith('hp-ux')
|
||||
|
||||
# Some code parts are similar to several unix platforms (e.g. Linux, Solaris, AIX).
|
||||
# macOS is not considered as unix since there are many platform-specific details for Mac in PyInstaller.
|
||||
is_unix = is_linux or is_solar or is_aix or is_freebsd or is_hpux or is_openbsd
|
||||
|
||||
# Linux distributions such as Alpine or OpenWRT use musl as their libc implementation and resultantly need specially
|
||||
# compiled bootloaders. On musl systems, ldd with no arguments prints 'musl' and its version.
|
||||
is_musl = is_linux and "musl" in subprocess.run(["ldd"], capture_output=True, encoding="utf-8").stderr
|
||||
|
||||
# Termux - terminal emulator and Linux environment app for Android.
|
||||
# With python >= 3.13, this could also be directly inferred from `sys.platform` or `platform.system()` (see PEP-738),
|
||||
# and `is_android` will also be set to True; this is not the case with earlier python versions (that people might still
|
||||
# have installed in their Termux environments), so for now, we keep the legacy check.
|
||||
is_termux = is_linux and hasattr(sys, 'getandroidapilevel')
|
||||
|
||||
# macOS version
|
||||
_macos_ver = tuple(int(x) for x in platform.mac_ver()[0].split('.')) if is_darwin else None
|
||||
|
||||
# macOS 11 (Big Sur): if python is not compiled with Big Sur support, it ends up in compatibility mode by default, which
|
||||
# is indicated by platform.mac_ver() returning '10.16'. The lack of proper Big Sur support breaks find_library()
|
||||
# function from ctypes.util module, as starting with Big Sur, shared libraries are not visible on disk anymore. Support
|
||||
# for the new library search mechanism was added in python 3.9 when compiled with Big Sur support. In such cases,
|
||||
# platform.mac_ver() reports version as '11.x'. The behavior can be further modified via SYSTEM_VERSION_COMPAT
|
||||
# environment variable; which allows explicitly enabling or disabling the compatibility mode. However, note that
|
||||
# disabling the compatibility mode and using python that does not properly support Big Sur still leaves find_library()
|
||||
# broken (which is a scenario that we ignore at the moment).
|
||||
# The same logic applies to macOS 12 (Monterey).
|
||||
is_macos_11_compat = bool(_macos_ver) and _macos_ver[0:2] == (10, 16) # Big Sur or newer in compat mode
|
||||
is_macos_11_native = bool(_macos_ver) and _macos_ver[0:2] >= (11, 0) # Big Sur or newer in native mode
|
||||
is_macos_11 = is_macos_11_compat or is_macos_11_native # Big Sur or newer
|
||||
|
||||
# Check if python >= 3.13 was built with Py_GIL_DISABLED / free-threading (PEP703).
|
||||
#
|
||||
# This affects the shared library name, which has the "t" ABI suffix, as per:
|
||||
# https://github.com/python/steering-council/issues/221#issuecomment-1841593283
|
||||
#
|
||||
# It also affects the layout of PyConfig structure used by bootloader; consequently we need to inform bootloader what
|
||||
# kind of build it is dealing with (only in python 3.13; with 3.14 and later, we use PEP741 configuration API in the
|
||||
# bootloader, and do not need to know the layout of PyConfig structure anymore)
|
||||
is_nogil = bool(sysconfig.get_config_var('Py_GIL_DISABLED'))
|
||||
|
||||
# In a virtual environment created by virtualenv (github.com/pypa/virtualenv) there exists sys.real_prefix with the path
|
||||
# to the base Python installation from which the virtual environment was created. This is true regardless of the version
|
||||
# of Python used to execute the virtualenv command.
|
||||
#
|
||||
# In a virtual environment created by the venv module available in the Python standard lib, there exists sys.base_prefix
|
||||
# with the path to the base implementation. This does not exist in a virtual environment created by virtualenv.
|
||||
#
|
||||
# The following code creates compat.is_venv and is.virtualenv that are True when running a virtual environment, and also
|
||||
# compat.base_prefix with the path to the base Python installation.
|
||||
|
||||
base_prefix: str = os.path.abspath(getattr(sys, 'real_prefix', getattr(sys, 'base_prefix', sys.prefix)))
|
||||
# Ensure `base_prefix` is not containing any relative parts.
|
||||
is_venv = is_virtualenv = base_prefix != os.path.abspath(sys.prefix)
|
||||
|
||||
# Conda environments sometimes have different paths or apply patches to packages that can affect how a hook or package
|
||||
# should access resources. Method for determining conda taken from https://stackoverflow.com/questions/47610844#47610844
|
||||
is_conda = os.path.isdir(os.path.join(base_prefix, 'conda-meta'))
|
||||
|
||||
# Similar to ``is_conda`` but is ``False`` using another ``venv``-like manager on top. In this case, no packages
|
||||
# encountered will be conda packages meaning that the default non-conda behaviour is generally desired from PyInstaller.
|
||||
is_pure_conda = os.path.isdir(os.path.join(sys.prefix, 'conda-meta'))
|
||||
|
||||
# Full path to python interpreter.
|
||||
python_executable = getattr(sys, '_base_executable', sys.executable)
|
||||
|
||||
# Is this Python from Microsoft App Store (Windows only)? Python from Microsoft App Store has executable pointing at
|
||||
# empty shims.
|
||||
is_ms_app_store = is_win and os.path.getsize(python_executable) == 0
|
||||
|
||||
if is_ms_app_store:
|
||||
# Locate the actual executable inside base_prefix.
|
||||
python_executable = os.path.join(base_prefix, os.path.basename(python_executable))
|
||||
if not os.path.exists(python_executable):
|
||||
raise SystemExit(
|
||||
'ERROR: PyInstaller cannot locate real python executable belonging to Python from Microsoft App Store!'
|
||||
)
|
||||
|
||||
# Bytecode magic value
|
||||
BYTECODE_MAGIC = importlib.util.MAGIC_NUMBER
|
||||
|
||||
# List of suffixes for Python C extension modules.
|
||||
EXTENSION_SUFFIXES = importlib.machinery.EXTENSION_SUFFIXES
|
||||
ALL_SUFFIXES = importlib.machinery.all_suffixes()
|
||||
|
||||
# On Windows we require pywin32-ctypes.
|
||||
# -> all pyinstaller modules should use win32api from PyInstaller.compat to
|
||||
# ensure that it can work on MSYS2 (which requires pywin32-ctypes)
|
||||
if is_win:
|
||||
if _setup_py_mode:
|
||||
pywintypes = None
|
||||
win32api = None
|
||||
else:
|
||||
try:
|
||||
# Hide the `cffi` package from win32-ctypes by temporarily blocking its import. This ensures that `ctypes`
|
||||
# backend is always used, even if `cffi` is available. The `cffi` backend uses `pycparser`, which is
|
||||
# incompatible with -OO mode (2nd optimization level) due to its removal of docstrings.
|
||||
# See https://github.com/pyinstaller/pyinstaller/issues/6345
|
||||
# On the off chance that `cffi` has already been imported, store the `sys.modules` entry so we can restore
|
||||
# it after importing `pywin32-ctypes` modules.
|
||||
orig_cffi = sys.modules.get('cffi')
|
||||
sys.modules['cffi'] = None
|
||||
|
||||
from win32ctypes.pywin32 import pywintypes # noqa: F401, E402
|
||||
from win32ctypes.pywin32 import win32api # noqa: F401, E402
|
||||
except ImportError as e:
|
||||
raise SystemExit(
|
||||
'ERROR: Could not import `pywintypes` or `win32api` from `win32ctypes.pywin32`.\n'
|
||||
'Please make sure that `pywin32-ctypes` is installed and importable, for example:\n\n'
|
||||
'pip install pywin32-ctypes\n'
|
||||
) from e
|
||||
finally:
|
||||
# Unblock `cffi`.
|
||||
if orig_cffi is not None:
|
||||
sys.modules['cffi'] = orig_cffi
|
||||
else:
|
||||
del sys.modules['cffi']
|
||||
del orig_cffi
|
||||
|
||||
# macOS's platform.architecture() can be buggy, so we do this manually here. Based off the python documentation:
|
||||
# https://docs.python.org/3/library/platform.html#platform.architecture
|
||||
if is_darwin:
|
||||
architecture = '64bit' if sys.maxsize > 2**32 else '32bit'
|
||||
else:
|
||||
architecture = platform.architecture()[0]
|
||||
|
||||
# Cygwin needs special handling, because platform.system() contains identifiers such as MSYS_NT-10.0-19042 and
|
||||
# CYGWIN_NT-10.0-19042 that do not fit PyInstaller's OS naming scheme. Explicitly set `system` to 'Cygwin'.
|
||||
system = 'Cygwin' if is_cygwin else platform.system()
|
||||
# Similarly, fold Android (reported by python >= 3.13 in Termux environment) back into Linux.
|
||||
if system == 'Android':
|
||||
system = 'Linux'
|
||||
|
||||
# Machine suffix for bootloader.
|
||||
if is_win:
|
||||
# On Windows ARM64 using an x64 Python environment, platform.machine() returns ARM64 but
|
||||
# we really want the bootloader that matches the Python environment instead of the OS.
|
||||
machine = _pyi_machine(os.environ.get("PROCESSOR_ARCHITECTURE", platform.machine()), platform.system())
|
||||
else:
|
||||
machine = _pyi_machine(platform.machine(), platform.system())
|
||||
|
||||
|
||||
# Wine detection and support
|
||||
def is_wine_dll(filename: str | os.PathLike):
|
||||
"""
|
||||
Check if the given PE file is a Wine DLL (PE-converted built-in, or fake/placeholder one).
|
||||
|
||||
Returns True if the given file is a Wine DLL, False if not (or if file cannot be analyzed or does not exist).
|
||||
"""
|
||||
_WINE_SIGNATURES = (
|
||||
b'Wine builtin DLL', # PE-converted Wine DLL
|
||||
b'Wine placeholder DLL', # Fake/placeholder Wine DLL
|
||||
)
|
||||
_MAX_LEN = max([len(sig) for sig in _WINE_SIGNATURES])
|
||||
|
||||
# Wine places their DLL signature in the padding area between the IMAGE_DOS_HEADER and IMAGE_NT_HEADERS. So we need
|
||||
# to compare the bytes that come right after IMAGE_DOS_HEADER, i.e., after initial 64 bytes. We can read the file
|
||||
# directly and avoid using the pefile library to avoid performance penalty associated with full header parsing.
|
||||
try:
|
||||
with open(filename, 'rb') as fp:
|
||||
fp.seek(64)
|
||||
signature = fp.read(_MAX_LEN)
|
||||
return signature.startswith(_WINE_SIGNATURES)
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
if is_win:
|
||||
try:
|
||||
import ctypes.util # noqa: E402
|
||||
is_win_wine = is_wine_dll(ctypes.util.find_library('kernel32'))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Set and get environment variables does not handle unicode strings correctly on Windows.
|
||||
|
||||
# Acting on os.environ instead of using getenv()/setenv()/unsetenv(), as suggested in
|
||||
# <http://docs.python.org/library/os.html#os.environ>: "Calling putenv() directly does not change os.environ, so it is
|
||||
# better to modify os.environ." (Same for unsetenv.)
|
||||
|
||||
|
||||
def getenv(name: str, default: str | None = None):
|
||||
"""
|
||||
Returns unicode string containing value of environment variable 'name'.
|
||||
"""
|
||||
return os.environ.get(name, default)
|
||||
|
||||
|
||||
def setenv(name: str, value: str):
|
||||
"""
|
||||
Accepts unicode string and set it as environment variable 'name' containing value 'value'.
|
||||
"""
|
||||
os.environ[name] = value
|
||||
|
||||
|
||||
def unsetenv(name: str):
|
||||
"""
|
||||
Delete the environment variable 'name'.
|
||||
"""
|
||||
# Some platforms (e.g., AIX) do not support `os.unsetenv()` and thus `del os.environ[name]` has no effect on the
|
||||
# real environment. For this case, we set the value to the empty string.
|
||||
os.environ[name] = ""
|
||||
del os.environ[name]
|
||||
|
||||
|
||||
# Exec commands in subprocesses.
|
||||
|
||||
|
||||
def exec_command(
|
||||
*cmdargs: str, encoding: str | None = None, raise_enoent: bool | None = None, **kwargs: int | bool | list | None
|
||||
):
|
||||
"""
|
||||
Run the command specified by the passed positional arguments, optionally configured by the passed keyword arguments.
|
||||
|
||||
.. DANGER::
|
||||
**Ignore this function's return value** -- unless this command's standard output contains _only_ pathnames, in
|
||||
which case this function returns the correct filesystem-encoded string expected by PyInstaller. In all other
|
||||
cases, this function's return value is _not_ safely usable.
|
||||
|
||||
For backward compatibility, this function's return value non-portably depends on the current Python version and
|
||||
passed keyword arguments:
|
||||
|
||||
* Under Python 3.x, this value is a **decoded `str` string**. However, even this value is _not_ necessarily
|
||||
safely usable:
|
||||
* If the `encoding` parameter is passed, this value is guaranteed to be safely usable.
|
||||
* Else, this value _cannot_ be safely used for any purpose (e.g., string manipulation or parsing), except to be
|
||||
passed directly to another non-Python command. Why? Because this value has been decoded with the encoding
|
||||
specified by `sys.getfilesystemencoding()`, the encoding used by `os.fsencode()` and `os.fsdecode()` to
|
||||
convert from platform-agnostic to platform-specific pathnames. This is _not_ necessarily the encoding with
|
||||
which this command's standard output was encoded. Cue edge-case decoding exceptions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cmdargs :
|
||||
Variadic list whose:
|
||||
1. Mandatory first element is the absolute path, relative path, or basename in the current `${PATH}` of the
|
||||
command to run.
|
||||
2. Optional remaining elements are arguments to pass to this command.
|
||||
encoding : str, optional
|
||||
Optional keyword argument specifying the encoding with which to decode this command's standard output under
|
||||
Python 3. As this function's return value should be ignored, this argument should _never_ be passed.
|
||||
raise_enoent : boolean, optional
|
||||
Optional keyword argument to simply raise the exception if the executing the command fails since to the command
|
||||
is not found. This is useful to checking id a command exists.
|
||||
|
||||
All remaining keyword arguments are passed as is to the `subprocess.Popen()` constructor.
|
||||
|
||||
Returns
|
||||
----------
|
||||
str
|
||||
Ignore this value. See discussion above.
|
||||
"""
|
||||
|
||||
proc = subprocess.Popen(cmdargs, stdout=subprocess.PIPE, **kwargs)
|
||||
try:
|
||||
out = proc.communicate(timeout=60)[0]
|
||||
except OSError as e:
|
||||
if raise_enoent and e.errno == errno.ENOENT:
|
||||
raise
|
||||
print('--' * 20, file=sys.stderr)
|
||||
print("Error running '%s':" % " ".join(cmdargs), file=sys.stderr)
|
||||
print(e, file=sys.stderr)
|
||||
print('--' * 20, file=sys.stderr)
|
||||
raise ExecCommandFailed("ERROR: Executing command failed!") from e
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.kill()
|
||||
raise
|
||||
|
||||
# stdout/stderr are returned as a byte array NOT as string, so we need to convert that to proper encoding.
|
||||
try:
|
||||
if encoding:
|
||||
out = out.decode(encoding)
|
||||
else:
|
||||
# If no encoding is given, assume we are reading filenames from stdout only because it is the common case.
|
||||
out = os.fsdecode(out)
|
||||
except UnicodeDecodeError as e:
|
||||
# The sub-process used a different encoding; provide more information to ease debugging.
|
||||
print('--' * 20, file=sys.stderr)
|
||||
print(str(e), file=sys.stderr)
|
||||
print('These are the bytes around the offending byte:', file=sys.stderr)
|
||||
print('--' * 20, file=sys.stderr)
|
||||
raise
|
||||
return out
|
||||
|
||||
|
||||
def exec_command_rc(*cmdargs: str, **kwargs: float | bool | list | None):
|
||||
"""
|
||||
Return the exit code of the command specified by the passed positional arguments, optionally configured by the
|
||||
passed keyword arguments.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cmdargs : list
|
||||
Variadic list whose:
|
||||
1. Mandatory first element is the absolute path, relative path, or basename in the current `${PATH}` of the
|
||||
command to run.
|
||||
2. Optional remaining elements are arguments to pass to this command.
|
||||
|
||||
All keyword arguments are passed as is to the `subprocess.call()` function.
|
||||
|
||||
Returns
|
||||
----------
|
||||
int
|
||||
This command's exit code as an unsigned byte in the range `[0, 255]`, where 0 signifies success and all other
|
||||
values signal a failure.
|
||||
"""
|
||||
|
||||
# 'encoding' keyword is not supported for 'subprocess.call'; remove it from kwargs.
|
||||
if 'encoding' in kwargs:
|
||||
kwargs.pop('encoding')
|
||||
return subprocess.call(cmdargs, **kwargs)
|
||||
|
||||
|
||||
def exec_command_all(*cmdargs: str, encoding: str | None = None, **kwargs: int | bool | list | None):
|
||||
"""
|
||||
Run the command specified by the passed positional arguments, optionally configured by the passed keyword arguments.
|
||||
|
||||
.. DANGER::
|
||||
**Ignore this function's return value.** If this command's standard output consists solely of pathnames, consider
|
||||
calling `exec_command()`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cmdargs : str
|
||||
Variadic list whose:
|
||||
1. Mandatory first element is the absolute path, relative path, or basename in the current `${PATH}` of the
|
||||
command to run.
|
||||
2. Optional remaining elements are arguments to pass to this command.
|
||||
encoding : str, optional
|
||||
Optional keyword argument specifying the encoding with which to decode this command's standard output. As this
|
||||
function's return value should be ignored, this argument should _never_ be passed.
|
||||
|
||||
All remaining keyword arguments are passed as is to the `subprocess.Popen()` constructor.
|
||||
|
||||
Returns
|
||||
----------
|
||||
(int, str, str)
|
||||
Ignore this 3-element tuple `(exit_code, stdout, stderr)`. See the `exec_command()` function for discussion.
|
||||
"""
|
||||
proc = subprocess.Popen(
|
||||
cmdargs,
|
||||
bufsize=-1, # Default OS buffer size.
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
**kwargs
|
||||
)
|
||||
# Waits for subprocess to complete.
|
||||
try:
|
||||
out, err = proc.communicate(timeout=60)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.kill()
|
||||
raise
|
||||
# stdout/stderr are returned as a byte array NOT as string. Thus we need to convert that to proper encoding.
|
||||
try:
|
||||
if encoding:
|
||||
out = out.decode(encoding)
|
||||
err = err.decode(encoding)
|
||||
else:
|
||||
# If no encoding is given, assume we're reading filenames from stdout only because it's the common case.
|
||||
out = os.fsdecode(out)
|
||||
err = os.fsdecode(err)
|
||||
except UnicodeDecodeError as e:
|
||||
# The sub-process used a different encoding, provide more information to ease debugging.
|
||||
print('--' * 20, file=sys.stderr)
|
||||
print(str(e), file=sys.stderr)
|
||||
print('These are the bytes around the offending byte:', file=sys.stderr)
|
||||
print('--' * 20, file=sys.stderr)
|
||||
raise
|
||||
|
||||
return proc.returncode, out, err
|
||||
|
||||
|
||||
def __wrap_python(args, kwargs):
|
||||
cmdargs = [sys.executable]
|
||||
|
||||
# macOS supports universal binaries (binary for multiple architectures. We need to ensure that subprocess
|
||||
# binaries are running for the same architecture as python executable. It is necessary to run binaries with 'arch'
|
||||
# command.
|
||||
if is_darwin:
|
||||
if architecture == '64bit':
|
||||
if platform.machine() == 'arm64':
|
||||
py_prefix = ['arch', '-arm64'] # Apple M1
|
||||
else:
|
||||
py_prefix = ['arch', '-x86_64'] # Intel
|
||||
elif architecture == '32bit':
|
||||
py_prefix = ['arch', '-i386']
|
||||
else:
|
||||
py_prefix = []
|
||||
# Since macOS 10.11, the environment variable DYLD_LIBRARY_PATH is no more inherited by child processes, so we
|
||||
# proactively propagate the current value using the `-e` option of the `arch` command.
|
||||
if 'DYLD_LIBRARY_PATH' in os.environ:
|
||||
path = os.environ['DYLD_LIBRARY_PATH']
|
||||
py_prefix += ['-e', 'DYLD_LIBRARY_PATH=%s' % path]
|
||||
cmdargs = py_prefix + cmdargs
|
||||
|
||||
if not __debug__:
|
||||
cmdargs.append('-O')
|
||||
|
||||
cmdargs.extend(args)
|
||||
|
||||
env = kwargs.get('env')
|
||||
if env is None:
|
||||
env = dict(**os.environ)
|
||||
|
||||
# Ensure python 3 subprocess writes 'str' as utf-8
|
||||
env['PYTHONIOENCODING'] = 'UTF-8'
|
||||
# ... and ensure we read output as utf-8
|
||||
kwargs['encoding'] = 'UTF-8'
|
||||
|
||||
return cmdargs, kwargs
|
||||
|
||||
|
||||
def exec_python(*args: str, **kwargs: str | None):
|
||||
"""
|
||||
Wrap running python script in a subprocess.
|
||||
|
||||
Return stdout of the invoked command.
|
||||
"""
|
||||
cmdargs, kwargs = __wrap_python(args, kwargs)
|
||||
return exec_command(*cmdargs, **kwargs)
|
||||
|
||||
|
||||
def exec_python_rc(*args: str, **kwargs: str | None):
|
||||
"""
|
||||
Wrap running python script in a subprocess.
|
||||
|
||||
Return exit code of the invoked command.
|
||||
"""
|
||||
cmdargs, kwargs = __wrap_python(args, kwargs)
|
||||
return exec_command_rc(*cmdargs, **kwargs)
|
||||
|
||||
|
||||
# Path handling.
|
||||
|
||||
|
||||
# Site-packages functions - use native function if available.
|
||||
def getsitepackages(prefixes: list | None = None):
|
||||
"""
|
||||
Returns a list containing all global site-packages directories.
|
||||
|
||||
For each directory present in ``prefixes`` (or the global ``PREFIXES``), this function finds its `site-packages`
|
||||
subdirectory depending on the system environment, and returns a list of full paths.
|
||||
"""
|
||||
# This implementation was copied from the ``site`` module, python 3.7.3.
|
||||
sitepackages = []
|
||||
seen = set()
|
||||
|
||||
if prefixes is None:
|
||||
prefixes = [sys.prefix, sys.exec_prefix]
|
||||
|
||||
for prefix in prefixes:
|
||||
if not prefix or prefix in seen:
|
||||
continue
|
||||
seen.add(prefix)
|
||||
|
||||
if os.sep == '/':
|
||||
sitepackages.append(os.path.join(prefix, "lib", "python%d.%d" % sys.version_info[:2], "site-packages"))
|
||||
else:
|
||||
sitepackages.append(prefix)
|
||||
sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
|
||||
return sitepackages
|
||||
|
||||
|
||||
# Backported for virtualenv. Module 'site' in virtualenv might not have this attribute.
|
||||
getsitepackages = getattr(site, 'getsitepackages', getsitepackages)
|
||||
|
||||
|
||||
# Wrapper to load a module from a Python source file. This function loads import hooks when processing them.
|
||||
def importlib_load_source(name: str, pathname: str):
|
||||
# Import module from a file.
|
||||
mod_loader = importlib.machinery.SourceFileLoader(name, pathname)
|
||||
mod = types.ModuleType(mod_loader.name)
|
||||
mod.__file__ = mod_loader.get_filename() # Some hooks require __file__ attribute in their namespace
|
||||
mod_loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
# Patterns of module names that should be bundled into the base_library.zip to be available during bootstrap.
|
||||
# These modules include direct or indirect dependencies of encodings.* modules. The encodings modules must be
|
||||
# recursively included to set the I/O encoding during python startup. Similarly, this list should include
|
||||
# modules used by PyInstaller's bootstrap scripts and modules (loader/pyi*.py)
|
||||
|
||||
PY3_BASE_MODULES = {
|
||||
'_collections_abc',
|
||||
'_weakrefset',
|
||||
'abc',
|
||||
'codecs',
|
||||
'collections',
|
||||
'copyreg',
|
||||
'encodings',
|
||||
'enum',
|
||||
'functools',
|
||||
'genericpath', # dependency of os.path
|
||||
'io',
|
||||
'heapq',
|
||||
'keyword',
|
||||
'linecache',
|
||||
'locale',
|
||||
'ntpath', # dependency of os.path
|
||||
'operator',
|
||||
'os',
|
||||
'posixpath', # dependency of os.path
|
||||
're',
|
||||
'reprlib',
|
||||
'stat', # dependency of os.path
|
||||
'traceback', # for startup errors
|
||||
'types',
|
||||
'weakref',
|
||||
'warnings',
|
||||
}
|
||||
|
||||
if not is_py310:
|
||||
PY3_BASE_MODULES.add('_bootlocale')
|
||||
|
||||
if is_android and is_py313:
|
||||
PY3_BASE_MODULES.add('_android_support')
|
||||
PY3_BASE_MODULES.add('threading') # dependency of _android_support
|
||||
|
||||
if not is_py315:
|
||||
PY3_BASE_MODULES.add('sre_compile')
|
||||
PY3_BASE_MODULES.add('sre_constants')
|
||||
PY3_BASE_MODULES.add('sre_parse')
|
||||
|
||||
# Object types of Pure Python modules in modulegraph dependency graph.
|
||||
# Pure Python modules have code object (attribute co_code).
|
||||
PURE_PYTHON_MODULE_TYPES = {
|
||||
'SourceModule',
|
||||
'CompiledModule',
|
||||
'Package',
|
||||
'NamespacePackage',
|
||||
# Deprecated.
|
||||
# TODO Could these module types be removed?
|
||||
'FlatPackage',
|
||||
'ArchiveModule',
|
||||
}
|
||||
# Object types of special Python modules (built-in, run-time, namespace package) in modulegraph dependency graph that do
|
||||
# not have code object.
|
||||
SPECIAL_MODULE_TYPES = {
|
||||
# Omit AliasNode from here (and consequently from VALID_MODULE_TYPES), in order to prevent PyiModuleGraph from
|
||||
# running standard hooks for aliased modules.
|
||||
#'AliasNode',
|
||||
'BuiltinModule',
|
||||
'RuntimeModule',
|
||||
'RuntimePackage',
|
||||
|
||||
# PyInstaller handles scripts differently and not as standard Python modules.
|
||||
'Script',
|
||||
}
|
||||
# Object types of Binary Python modules (extensions, etc) in modulegraph dependency graph.
|
||||
BINARY_MODULE_TYPES = {
|
||||
'Extension',
|
||||
'ExtensionPackage',
|
||||
}
|
||||
# Object types of valid Python modules in modulegraph dependency graph.
|
||||
VALID_MODULE_TYPES = PURE_PYTHON_MODULE_TYPES | SPECIAL_MODULE_TYPES | BINARY_MODULE_TYPES
|
||||
# Object types of bad/missing/invalid Python modules in modulegraph dependency graph.
|
||||
# TODO: should be 'Invalid' module types also in the 'MISSING' set?
|
||||
BAD_MODULE_TYPES = {
|
||||
'BadModule',
|
||||
'ExcludedModule',
|
||||
'InvalidSourceModule',
|
||||
'InvalidCompiledModule',
|
||||
'MissingModule',
|
||||
|
||||
# Runtime modules and packages are technically valid rather than bad, but exist only in-memory rather than on-disk
|
||||
# (typically due to pre_safe_import_module() hooks), and hence cannot be physically frozen. For simplicity, these
|
||||
# nodes are categorized as bad rather than valid.
|
||||
'RuntimeModule',
|
||||
'RuntimePackage',
|
||||
}
|
||||
ALL_MODULE_TYPES = VALID_MODULE_TYPES | BAD_MODULE_TYPES
|
||||
# TODO: review this mapping to TOC, remove useless entries.
|
||||
# Dictionary to map ModuleGraph node types to TOC typecodes.
|
||||
MODULE_TYPES_TO_TOC_DICT = {
|
||||
# Pure modules.
|
||||
'AliasNode': 'PYMODULE',
|
||||
'Script': 'PYSOURCE',
|
||||
'SourceModule': 'PYMODULE',
|
||||
'CompiledModule': 'PYMODULE',
|
||||
'Package': 'PYMODULE',
|
||||
'FlatPackage': 'PYMODULE',
|
||||
'ArchiveModule': 'PYMODULE',
|
||||
# Binary modules.
|
||||
'Extension': 'EXTENSION',
|
||||
'ExtensionPackage': 'EXTENSION',
|
||||
# Special valid modules.
|
||||
'BuiltinModule': 'BUILTIN',
|
||||
'NamespacePackage': 'PYMODULE',
|
||||
# Bad modules.
|
||||
'BadModule': 'bad',
|
||||
'ExcludedModule': 'excluded',
|
||||
'InvalidSourceModule': 'invalid',
|
||||
'InvalidCompiledModule': 'invalid',
|
||||
'MissingModule': 'missing',
|
||||
'RuntimeModule': 'runtime',
|
||||
'RuntimePackage': 'runtime',
|
||||
# Other.
|
||||
'does not occur': 'BINARY',
|
||||
}
|
||||
|
||||
|
||||
def check_requirements():
|
||||
"""
|
||||
Verify that all requirements to run PyInstaller are met.
|
||||
|
||||
Fail hard if any requirement is not met.
|
||||
"""
|
||||
# Fail hard if Python does not have minimum required version
|
||||
if sys.version_info < (3, 8):
|
||||
raise EnvironmentError('PyInstaller requires Python 3.8 or newer.')
|
||||
|
||||
if sys.implementation.name != "cpython":
|
||||
raise SystemExit(f"ERROR: PyInstaller does not support {sys.implementation.name}. Only CPython is supported.")
|
||||
|
||||
if getattr(sys, "frozen", False):
|
||||
raise SystemExit("ERROR: PyInstaller can not be ran on itself")
|
||||
|
||||
# There are some old packages which used to be backports of libraries which are now part of the standard library.
|
||||
# These backports are now unmaintained and contain only an older subset of features leading to obscure errors like
|
||||
# "enum has not attribute IntFlag" if installed.
|
||||
from importlib.metadata import distribution, PackageNotFoundError
|
||||
|
||||
for name in ["enum34", "typing", "pathlib"]:
|
||||
try:
|
||||
dist = distribution(name)
|
||||
except PackageNotFoundError:
|
||||
continue
|
||||
remove = "conda remove" if is_conda else f'"{sys.executable}" -m pip uninstall {name}'
|
||||
raise SystemExit(
|
||||
f"ERROR: The '{name}' package is an obsolete backport of a standard library package and is incompatible "
|
||||
f"with PyInstaller. Please remove this package (located in {dist.locate_file('')}) using\n {remove}\n"
|
||||
"then try again."
|
||||
)
|
||||
|
||||
# Bail out if binutils is not installed.
|
||||
if is_linux and shutil.which("objdump") is None:
|
||||
raise SystemExit(
|
||||
"ERROR: On Linux, objdump is required. It is typically provided by the 'binutils' package "
|
||||
"installable via your Linux distribution's package manager."
|
||||
)
|
||||
56
venv/lib/python3.12/site-packages/PyInstaller/config.py
Normal file
56
venv/lib/python3.12/site-packages/PyInstaller/config.py
Normal file
@@ -0,0 +1,56 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
This module holds run-time PyInstaller configuration.
|
||||
|
||||
Variable CONF is a dict() with all configuration options that are necessary for the build phase. Build phase is done by
|
||||
passing .spec file to exec() function. CONF variable is the only way how to pass arguments to exec() and how to avoid
|
||||
using 'global' variables.
|
||||
|
||||
NOTE: Having 'global' variables does not play well with the test suite because it does not provide isolated environments
|
||||
for tests. Some tests might fail in this case.
|
||||
|
||||
NOTE: The 'CONF' dict() is cleaned after building phase to not interfere with any other possible test.
|
||||
|
||||
To pass any arguments to build phase, just do:
|
||||
|
||||
from PyInstaller.config import CONF
|
||||
CONF['my_var_name'] = my_value
|
||||
|
||||
And to use this variable in the build phase:
|
||||
|
||||
from PyInstaller.config import CONF
|
||||
foo = CONF['my_var_name']
|
||||
|
||||
|
||||
This is the list of known variables. (Please update it if necessary.)
|
||||
|
||||
cachedir
|
||||
hiddenimports
|
||||
noconfirm
|
||||
pathex
|
||||
ui_admin
|
||||
ui_access
|
||||
upx_available
|
||||
upx_dir
|
||||
workpath
|
||||
|
||||
tests_modgraph - cached PyiModuleGraph object to speed up tests
|
||||
|
||||
code_cache - dictionary associating `Analysis.pure` list instances with code cache dictionaries. Used by PYZ writer.
|
||||
"""
|
||||
|
||||
# NOTE: Do not import other PyInstaller modules here. Just define constants here.
|
||||
|
||||
CONF = {
|
||||
# Unit tests require this key to exist.
|
||||
'pathex': [],
|
||||
}
|
||||
107
venv/lib/python3.12/site-packages/PyInstaller/configure.py
Normal file
107
venv/lib/python3.12/site-packages/PyInstaller/configure.py
Normal file
@@ -0,0 +1,107 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
"""
|
||||
Configure PyInstaller for the current Python installation.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from PyInstaller import compat
|
||||
from PyInstaller import log as logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _check_upx_availability(upx_dir):
|
||||
logger.debug('Testing UPX availability ...')
|
||||
|
||||
upx_exe = "upx"
|
||||
if upx_dir:
|
||||
upx_exe = os.path.normpath(os.path.join(upx_dir, upx_exe))
|
||||
|
||||
# Check if we can call `upx -V`.
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[upx_exe, '-V'],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
encoding='utf-8',
|
||||
)
|
||||
except Exception:
|
||||
logger.debug('UPX is not available.')
|
||||
return False
|
||||
|
||||
# Read the first line to display version string
|
||||
try:
|
||||
version_string = output.splitlines()[0]
|
||||
except IndexError:
|
||||
version_string = 'version string unavailable'
|
||||
|
||||
logger.debug('UPX is available: %s', version_string)
|
||||
return True
|
||||
|
||||
|
||||
def _get_pyinstaller_cache_dir():
|
||||
old_cache_dir = None
|
||||
if compat.getenv('PYINSTALLER_CONFIG_DIR'):
|
||||
cache_dir = compat.getenv('PYINSTALLER_CONFIG_DIR')
|
||||
elif compat.is_win:
|
||||
cache_dir = compat.getenv('LOCALAPPDATA')
|
||||
if not cache_dir:
|
||||
cache_dir = os.path.expanduser('~\\Application Data')
|
||||
elif compat.is_darwin:
|
||||
cache_dir = os.path.expanduser('~/Library/Application Support')
|
||||
else:
|
||||
# According to XDG specification: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
old_cache_dir = compat.getenv('XDG_DATA_HOME')
|
||||
if not old_cache_dir:
|
||||
old_cache_dir = os.path.expanduser('~/.local/share')
|
||||
cache_dir = compat.getenv('XDG_CACHE_HOME')
|
||||
if not cache_dir:
|
||||
cache_dir = os.path.expanduser('~/.cache')
|
||||
cache_dir = os.path.join(cache_dir, 'pyinstaller')
|
||||
# Move old cache-dir, if any, to new location.
|
||||
if old_cache_dir and not os.path.exists(cache_dir):
|
||||
old_cache_dir = os.path.join(old_cache_dir, 'pyinstaller')
|
||||
if os.path.exists(old_cache_dir):
|
||||
parent_dir = os.path.dirname(cache_dir)
|
||||
if not os.path.exists(parent_dir):
|
||||
os.makedirs(parent_dir)
|
||||
os.rename(old_cache_dir, cache_dir)
|
||||
return cache_dir
|
||||
|
||||
|
||||
def get_config(upx_dir=None):
|
||||
config = {}
|
||||
|
||||
config['cachedir'] = _get_pyinstaller_cache_dir()
|
||||
config['upx_dir'] = upx_dir
|
||||
|
||||
# Disable UPX on non-Windows. Using UPX (3.96) on modern Linux shared libraries (for example, the python3.x.so
|
||||
# shared library) seems to result in segmentation fault when they are dlopen'd. This happens in recent versions
|
||||
# of Fedora and Ubuntu linux, as well as in Alpine containers. On macOS, UPX (3.96) fails with
|
||||
# UnknownExecutableFormatException on most .dylibs (and interferes with code signature on other occasions). And
|
||||
# even when it would succeed, compressed libraries cannot be (re)signed due to failed strict validation.
|
||||
upx_available = _check_upx_availability(upx_dir)
|
||||
if upx_available:
|
||||
if compat.is_win or compat.is_cygwin:
|
||||
logger.info("UPX is available and will be used if enabled on build targets.")
|
||||
elif os.environ.get("PYINSTALLER_FORCE_UPX", "0") != "0":
|
||||
logger.warning(
|
||||
"UPX is available and force-enabled on platform with known compatibility problems - use at own risk!"
|
||||
)
|
||||
else:
|
||||
upx_available = False
|
||||
logger.info("UPX is available but is disabled on non-Windows due to known compatibility problems.")
|
||||
config['upx_available'] = upx_available
|
||||
|
||||
return config
|
||||
@@ -0,0 +1 @@
|
||||
#
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user